開發平台:
高通 android 7.1
需求:
app不帶系統簽名,僅僅以反射的方式調用PackageManager類的installPackage、deletePackage方法,實作靜默安裝解除安裝。
背景知識:
正常情況下,靜默安裝解除安裝需要以下2個系統級權限+系統簽名
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
解決思路如下:
首先,沒有系統簽名的情況下,安裝app的時候這2個權限不會放開,是以先修改PackageManagerService.java->grantPermissionsLPw方法
+ //允許app全部權限
+ mPkgList.add("com.android.testa");
+ mPkgList.add("com.android.testb");
+ if (mPkgList.contains(pkg.packageName)) {
+ final int permsSize = pkg.requestedPermissions.size();
+ for (int i = 0; i < permsSize; i++) {
+ final String name = pkg.requestedPermissions.get(i);
+ final BasePermission bp = mSettings.mPermissions.get(name);
+ if (null != bp && permissionsState.grantInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ Slog.d(TAG, "grant permission " + name + " to package " + pkg.packageName);
+ changedInstallPermission = true;
+ }
+ }
+ }
+
if ((changedInstallPermission || replace) && !ps.installPermissionsFixed &&
!isSystemApp(ps) || isUpdatedSystemApp(ps)){
// This is the first that we have heard about this package, so the
這一步修改後,mmm frameworks/base/services,然後重新替換/system/framework/service.jar驗證,靜默安裝ok,靜默解除安裝不ok,但是log裡面沒有顯示權限問題,說明權限實際上已經具備,其他地方出了問題。
繼續讀源碼,追蹤代碼繼承關系和調用邏輯:
1.PackageManagerService.java繼承IPackageManager.Stub
2.ApplicationPackageManager.java繼承PackageManager.java
3.反射調用PackageManager.java的installPackage deletePackage方法,實際上是調用ApplicationPackageManager.java裡面的方法
ApplicationPackageManager.java裡面的對應源碼:
/** @hide */
public class ApplicationPackageManager extends PackageManager {
//install
@Override
public void installPackage(Uri packageURI, PackageInstallObserver observer,
int flags, String installerPackageName) {
installCommon(packageURI, observer, flags, installerPackageName, mContext.getUserId());
}
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
int userId) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
final String originPath = packageURI.getPath();
try {
mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//delete
@Override
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
deletePackageAsUser(packageName, observer, flags, mContext.getUserId());
}
@Override
public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
int userId) {
try {
mPM.deletePackageAsUser(packageName, observer, userId, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//實際上是通過aidl調用mPM的方法,mPM是一個IPackageManager對象,在建立ApplicationPackageManager對象的時候指派
ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}
4.從ApplicationPackageManager源碼可以看到,安裝解除安裝是通過aidl調用mPM的方法,mPM是一個IPackageManager對象,在建立ApplicationPackageManager對象的時候指派。
5.PackageManagerService.java繼承IPackageManager.Stub,實際上就調用了PackageManagerService.java裡面的installPackageAsUser和deletePackageAsUser方法,是以本質上靜默安裝解除安裝都是在PackageManagerService.java裡面實作的。
6.仔細檢視PackageManagerService.java解除安裝的代碼邏輯,注意看isCallerAllowedToSilentlyUninstall方法,從命名就可以看出,這裡是判斷是否允許調用者執行靜默解除安裝,顯然第三方app在預設情況下是傳回的false,是以無法繼續執行靜默解除安裝的邏輯,并且從PMS源碼看,不會抛出什麼異常資訊,實測加列印确實如此。
@Override
public void deletePackage(final String packageName,
final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
//如果app的清單檔案裡面沒有申明解除安裝權限,那麼這一句會抛出異常,提示沒有權限
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(observer);
final int uid = Binder.getCallingUid();
if (!isOrphaned(packageName)
&& !isCallerAllowedToSilentlyUninstall(uid, packageName)) {
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;
}
...省略
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID
|| 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;
}
// Allow package verifier to silently uninstall.
if (mRequiredVerifierPackage != null &&
callingUid == getPackageUid(mRequiredVerifierPackage, 0, callingUserId)) {
return true;
}
// Allow package uninstaller to silently uninstall.
if (mRequiredUninstallerPackage != null &&
callingUid == getPackageUid(mRequiredUninstallerPackage, 0, callingUserId)) {
return true;
}
// Allow storage manager to silently uninstall.
if (mStorageManagerPackage != null &&
callingUid == getPackageUid(mStorageManagerPackage, 0, callingUserId)) {
return true;
}
return false;
}
7.在isCallerAllowedToSilentlyUninstall方法裡面,增加特殊處理
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
+ if (mPkgList.size() > 0) {
+ try {
+ if (mPkgList.contains(AppGlobals.getPackageManager().getNameForUid(callingUid))) {
+ return true;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Slog.e(TAG, "getNameForUid error is " + e.getMessage());
+ }
+ }
加上以上修改之後實測,解除安裝正常。
還是得多讀源碼呀。。。RTFSC!!!
PS:
測試代碼如下:
//AndroidManiFest.xml裡面聲明權限
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
mPackageDeleteObserver = new PackageDeleteObserver();
mPackageInstallObserver = new PackageInstallObserver();
//解除安裝
try {
Class<?>[] uninstalltypes = new Class[]{String.class, IPackageDeleteObserver.class, int.class};
PackageManager pm = mContext.getPackageManager();
Method uninstall = pm.getClass().getMethod("deletePackage", uninstalltypes);
uninstall.invoke(pm, new Object[]{"cache.wind.nfc", mPackageDeleteObserver, 0});
Log.d(TAG, "invoke uninstall app>>>>>>>");
} catch (Exception e) {
Log.e(TAG, "error " + e.getMessage());
e.printStackTrace();
}
//安裝
try {
Class<?>[] installtypes = new Class[]{Uri.class, IPackageInstallObserver.class, int.class, String.class};
PackageManager pm = mContext.getPackageManager();
Method install = pm.getClass().getMethod("installPackage", installtypes);
int installFlags = 0;
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
install.invoke(pm, new Object[]{Uri.fromFile(new File("/sdcard/NfcReader.apk")), mPackageInstallObserver, installFlags, null});
Log.d(TAG, "invoke install app>>>>>>>");
} catch (Exception e) {
Log.e(TAG, "error " + e.getMessage());
e.printStackTrace();
}
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
@Override
public void packageDeleted(String s, int i) throws RemoteException {
Log.d(TAG, "packageDeleted s is " + s + " return code is " + i);
}
}
class PackageInstallObserver extends IPackageInstallObserver.Stub {
@Override
public void packageInstalled(String s, int i) throws RemoteException {
Log.d(TAG, "packageInstalled s is " + s + " return code is " + i);
}
}