前言
本文给予android8.1版本,通过修改PMS中卸载流程代码实现静默卸载
PackageInstaller
卸载从uninstall开始:
public void uninstall(@NonNull VersionedPackage versionedPackage, @DeleteFlags int flags,
@NonNull IntentSender statusReceiver) {
Preconditions.checkNotNull(versionedPackage, "versionedPackage cannot be null");
try {
mInstaller.uninstall(versionedPackage, mInstallerPackageName,
flags, statusReceiver, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
PackageInstallerService
@Override
public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) throws RemoteException {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
// Check whether the caller is device owner, in which case we do it silently.
DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
callerPackageName);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(), isDeviceOwner, userId);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
} else if (isDeviceOwner) {
// Allow the DeviceOwner to silently delete packages
// Need to clear the calling identity to get DELETE_PACKAGES permission
long ident = Binder.clearCallingIdentity();
try {
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
} finally {
Binder.restoreCallingIdentity(ident);
}
} else {
// Take a short detour to confirm with user
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
adapter.onUserActionRequired(intent);
}
}
PackageManagerService
走到PMS的deletePackageVersioned
public void deletePackageVersioned(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
final boolean canViewInstantApps = canViewInstantApps(callingUid, userId);
Preconditions.checkNotNull(versionedPackage);
Preconditions.checkNotNull(observer);
Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(),
PackageManager.VERSION_CODE_HIGHEST,
Integer.MAX_VALUE, "versionCode must be >= -1");
final String packageName = versionedPackage.getPackageName();
final int versionCode = versionedPackage.getVersionCode();
final String internalPackageName;
synchronized (mPackages) {
// Normalize package name to handle renamed packages and static libs
internalPackageName = resolveInternalPackageNameLPr(versionedPackage.getPackageName(),
versionedPackage.getVersionCode());
}
final int uid = Binder.getCallingUid();
if (!isOrphaned(internalPackageName)
&& !isCallerAllowedToSilentlyUninstall(uid, internalPackageName)) {
try {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
observer.onUserActionRequired(intent);
} catch (RemoteException re) {
}
return;
}
…………
在isCallerAllowedToSilentlyUninstall中判断是否可以静默卸载
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return true;
}
final int callingUserId = UserHandle.getUserId(callingUid);
// If the caller installed the pkgName, then allow it to silently uninstall.
if (callingUid == getPackageUid(getInstallerPackageName(pkgName), 0, callingUserId)) {
return true;
}
…………
以上两种情况分别为发起卸载者为shell、root或者system的uid的情况;或者被卸载app为发起卸载者下载的情况。这两种情况可以静默卸载(当然之后还有几个条件,这里暂不分析)
如果想实现不满足这两种条件也能静默卸载,需要在这里多加一种允许静默卸载的条件:
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return true;
}
if (mPermissionWhiteList == null) {
mPermissionWhiteList = new ArrayList<String>();
scanPermissionWhiteList();
}
String[] uidPackages = getPackagesForUid(callingUid);
if (uidPackages != null) {
for (String pkg : uidPackages) {
if (pkg != null && mPermissionWhiteList.contains(pkg)) {
return true;
}
}
}
final int callingUserId = UserHandle.getUserId(callingUid);
// If the caller installed the pkgName, then allow it to silently uninstall.
if (callingUid == getPackageUid(getInstallerPackageName(pkgName), 0, callingUserId)) {
return true;
}
…………
private final static String PERMISSION_WHITE_LIST = "/system/etc/PermissionWhiteList.txt";
ArrayList<String> mPermissionWhiteList;
private void scanPermissionWhiteList() {
try{
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(PERMISSION_WHITE_LIST)));
String line ="";
while ((line = br.readLine()) != null){
if (DEBUG_PERMISSIONS) {
Slog.d(TAG, "PermissionWhiteList scan, line: " + line);
}
mPermissionWhiteList.add(line);
}
br.close();
} catch (java.io.FileNotFoundException ex){
Log.e(TAG, "FileNotFoundException ex: " + ex);
} catch (java.io.IOException ex){
Log.e(TAG, "IOException ex: " + ex);
}
}
之后在合适的Android.mk中将/system/etc/PermissionWhiteList.txt写入system.img烧写即可:
PRODUCT_COPY_FILES +=
frameworks/base/data/etc/PermissionWhiteList.txt:system/etc/PermissionWhiteList.txt