Android identifies a pre installed third party App method example

  • 2021-10-27 09:18:22
  • OfStack

Preface

When you buy a new mobile phone, there will be many App in it, some of which belong to the system App and cannot be uninstalled, and some belong to the third party App. The manufacturer will pre-install some App which are commonly used or give them advertising fees, which can be uninstalled.

If the system App is to be divided in detail, it can be divided in one step according to its different paths (e.g./system/app,/system/priv-app,/vendor/app, etc.). However, for developers, App installed on mobile phones can only be divided into two categories: system App and user App, which can be distinguished according to system API, so it will not be described in detail here. Simply put, the existence of ApplicationInfo.FLAG_SYSTEM or ApplicationInfo.FLAG_UPDATED_SYSTEM_APP flag is the system App, otherwise it is the user App.

However, using this method, those pre-installed App will also be included in the user App, so is there any way to know which of the user App are pre-installed and which are manually installed by the user?

Share one method here: The installation time of App is full second for the pre-installed third party App.

If you don't care why you can use this strange method to distinguish pre-installed App, you can close this article.

Before, I didn't know why I could use this method. At that time, I guessed that the time when the mobile phone started for the first time was inaccurate, and it would be January 1st of a certain year. Then, because each App directory would be scanned and App would be installed when starting, it was marked with such an installation time. But this explanation doesn't make sense. Even if this is the case, the installation time should not be the whole second. Therefore, I decided to find a good reason. The following is my process of verification.

Background knowledge

First, introduce some background knowledge.

The installation time of App can be obtained by obtaining PackageInfo, and its firstInstallTime attribute is the installation time.

/data/system/packages. xml saves the information of App installed on the phone, where the installation time of App is saved. I intercept two examples below.


<package name="com.tencent.mm" 
codePath="/data/app/com.tencent.mm-TSn6yG4fF7A_EaxE5OtrHQ==" 
nativeLibraryPath="/data/app/com.tencent.mm-TSn6yG4fF7A_EaxE5OtrHQ==/lib" 
primaryCpuAbi="armeabi" publicFlags="945307204" 
privateFlags="0" ft="167702c7508" it="1676feab448" 
ut="167702c8a57" version="1360" userId="10118">
...
</package>

<package name="com.android.providers.downloads" 
codePath="/system/priv-app/DownloadProvider" 
nativeLibraryPath="/system/priv-app/DownloadProvider/lib" publicFlags="944258629" 
privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" 
ut="11e8dc5d800" version="28" sharedUserId="10006" isOrphaned="true">
...
</package>

Among them, the value of it is the installation time, which is saved here in 106-ary system. The installation time of the above two App is converted into 10-ary system, which are 1543770911816 and 1230739200000 respectively, corresponding to Beijing time, namely 2018-12-03 01:15:11 and 2009-01-01 "Well... I can't believe I didn't sleep at 1 o'clock on December 3, and I installed a WeChat..."

When the system starts, PackageManagerService is started by SystemServer, and PackageManagerService scans directories such as/data/app,/system/app,/system/priv-app,/vendor/app, etc. It can be understood that Apk in these directories will be installed once, and PackageManagerService will be combined with packages.xml mentioned above.

Train of thought

According to the above knowledge, we can know that if packages. xml already has the information of an App, the installation time of this App must be the time recorded in packages. xml. The packages. xml file does not exist when the mobile phone is started for the first time, or when a new App is installed, there is no record of this App in packages. xml, that is to say, it is the key to confirm that firstInstallTime (i.e. it) in this packages. xml is generated if it is generated.

The following is based on the 7.0. 0_r1 version code.

By searching for PackageManagerService, there is one piece of code in the scanPackageDirtyLI method:


// Take care of first install / last update times.
if (currentTime != 0) {
 if (pkgSetting.firstInstallTime == 0) {
 pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
 } else if ((scanFlags&SCAN_UPDATE_TIME) != 0) {
 pkgSetting.lastUpdateTime = currentTime;
 }
} else if (pkgSetting.firstInstallTime == 0) {
 // We need *something*. Take time time stamp of the file.
 pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
} else if ((policyFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
 if (scanFileTime != pkgSetting.timeStamp) {
 // A package on the system image has changed; consider this
 // to be an update.
 pkgSetting.lastUpdateTime = scanFileTime;
 }
}

Where currentTime is a parameter of scanPackageDirtyLI method. pkgSetting is the information of the App read from packages. xml (PackageSetting object). If the information of the App does not exist in packages. xml, an PackageSetting is created based on the information parsed from Apk. scanFileTime is the last time the Apk file was modified.
You can see that there are several situations:

The incoming currentTime is not 0, and the read firstInstallTime from packages. xml is 0. This situation sets both firstInstallTime and lastUpdateTime to the value of the incoming currentTime. The incoming currentTime is not 0, and the incoming scanFlags has SCAN_UPDATE_TIME set. This situation sets lastUpdateTime to the value of the incoming currentTime. The incoming currentTime is 0, and the read firstInstallTime from packages. xml is 0. This situation sets both firstInstallTime and lastUpdateTime to the last modification time of Apk. The incoming currentTime is 0, the firstInstallTime read from packages. xml is not 0, the incoming policyFlags is set with PackageParser.PARSE_IS_SYSTEM_DIR, and scanFileTime is different from the timeStamp read from packages. xml (ft of package tag in packages). This situation sets lastUpdateTime to the last modification time of Apk.

Corresponding to the scene where we actually use mobile phones, the above four situations correspond to the following scenarios:

Case 1: Corresponding to the newly installed App. currentTime is the current timestamp, and firstInstallTime and lastUpdateTime of this newly installed App are set to the current timestamp. Case 2: Update App correspondingly. currentTime is the current timestamp, lastUpdateTime is set to the current timestamp, and firstInstallTime remains unchanged. Case 3: When the phone started, PackageManagerService scanned various directories and found App that did not exist in packages. xml (all App were not in packages. xml at the first startup). In the fourth case, the system update and other operations update the App of the system partition, resulting in the difference between the last modification time and record of its file, which will be considered as an update.

We can boldly guess that the third case will go when starting the mobile phone for the first time, so the installation time of the system App and the pre-installed App is the last modification time of the files, and the last modification time of these files is the whole second.

How to verify?

Let's first look at the last modification time of the Apk file above for com. android. providers. downloads.


# stat DownloadProvider.apk
 File: `DownloadProvider.apk'
 Size: 504712	 Blocks: 992	 IO Blocks: 512	regular file
Device: 10305h/66309d	 Inode: 1308	 Links: 1
Access: (644/-rw-r--r--)	Uid: ( 0/ root)	Gid: ( 0/ root)
Access: 2009-01-01 00:00:00.000000000
Modify: 2009-01-01 00:00:00.000000000
Change: 2009-01-01 00:00:00.000000000

Time and packages. xml saved time 1, is indeed the last time to modify the file as the installation time. Then there is one more question to confirm. Is the incoming currentTime 0?

Tracing back to the call chain, we can see in the constructor of PackageManagerService the method of scanning each directory. The last parameter 0 passed in by calling the scanDirTracedLI method is currentTime in the scanPackageDirtyLI method. Those who are interested can also take a closer look at which directories PackageManagerService scanned.


File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirTracedLI(vendorOverlayDir, mDefParseFlags
  | PackageParser.PARSE_IS_SYSTEM
  | PackageParser.PARSE_IS_SYSTEM_DIR
  | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir, mDefParseFlags
  | PackageParser.PARSE_IS_SYSTEM
  | PackageParser.PARSE_IS_SYSTEM_DIR
  | PackageParser.PARSE_IS_PRIVILEGED,
  scanFlags | SCAN_NO_DEX, 0);

// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
  | PackageParser.PARSE_IS_SYSTEM
  | PackageParser.PARSE_IS_SYSTEM_DIR
  | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir, mDefParseFlags
  | PackageParser.PARSE_IS_SYSTEM
  | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
 vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
 // failed to look up canonical path, continue with original one
}
scanDirTracedLI(vendorAppDir, mDefParseFlags
  | PackageParser.PARSE_IS_SYSTEM
  | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir, mDefParseFlags
  | PackageParser.PARSE_IS_SYSTEM
  | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

...

scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
  | PackageParser.PARSE_FORWARD_LOCK,
  scanFlags | SCAN_REQUIRE_KNOWN, 0);

If you are interested, you can follow 1 to install App and update App to see if the incoming currentTime is the current timestamp.

At this point, we have proved that when the mobile phone is started for the first time, The system will regard the last modification time of the file as the installation time of the system App and the pre-installed App. This time is generally a whole second similar to the above 2009-01-01 00:00:00. 00000000000 (as to why, that is another problem), and it is almost impossible for us to install App in a whole second, so we can distinguish the App pre-installed by mobile phone from App manually installed by users by whether the installation time is a whole second.

What's the use of distinguishing between pre-installed App and App manually installed by users? Please use your imagination, for example, a user's mobile phone only has one manually installed App or a few App in your home, so what good things does he want?

Summarize


Related articles: