package com.lody.virtual.server.pm; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.net.Uri; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Pair; import com.lody.virtual.client.core.InstallStrategy; import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.env.Constants; import com.lody.virtual.helper.compat.NativeLibraryHelperCompat; import com.lody.virtual.helper.compat.PackageParserCompat; import com.lody.virtual.helper.proto.AppSetting; import com.lody.virtual.helper.proto.InstallResult; import com.lody.virtual.helper.utils.FileUtils; import com.lody.virtual.helper.utils.ResourcesUtils; import com.lody.virtual.helper.utils.VLog; import com.lody.virtual.os.VEnvironment; import com.lody.virtual.os.VUserHandle; import com.lody.virtual.server.IAppManager; import com.lody.virtual.server.accounts.VAccountManagerService; import com.lody.virtual.server.am.BroadcastSystem; import com.lody.virtual.server.am.UidSystem; import com.lody.virtual.server.am.VActivityManagerService; import com.lody.virtual.server.interfaces.IAppObserver; import com.lody.virtual.server.interfaces.IAppRequestListener; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** * @author Lody */ public class VAppManagerService extends IAppManager.Stub { private static final String TAG = VAppManagerService.class.getSimpleName(); private static final AtomicReference<VAppManagerService> gService = new AtomicReference<>(); private final UidSystem mUidSystem = new UidSystem(); private boolean isBooting; private RemoteCallbackList<IAppObserver> mRemoteCallbackList = new RemoteCallbackList<IAppObserver>(); private IAppRequestListener listener; public static VAppManagerService get() { return gService.get(); } public static void systemReady() { VAppManagerService instance = new VAppManagerService(); instance.mUidSystem.initUidList(); gService.set(instance); } public boolean isBooting() { return isBooting; } public void preloadAllApps() { isBooting = true; for (File appDir : VEnvironment.getDataAppDirectory().listFiles()) { String pkgName = appDir.getName(); if ("android".equals(pkgName)) { continue; } File storeFile = new File(appDir, "base.apk"); int flags = 0; if (!storeFile.exists()) { ApplicationInfo appInfo = null; try { appInfo = VirtualCore.get().getUnHookPackageManager() .getApplicationInfo(pkgName, 0); } catch (PackageManager.NameNotFoundException e) { // Ignore } if (appInfo == null || appInfo.publicSourceDir == null) { FileUtils.deleteDir(appDir); for (int userId : VUserManagerService.get().getUserIds()) { FileUtils.deleteDir(VEnvironment.getDataUserPackageDirectory(userId, pkgName)); } continue; } storeFile = new File(appInfo.publicSourceDir); flags |= InstallStrategy.DEPEND_SYSTEM_IF_EXIST; } InstallResult res = install(storeFile.getPath(), flags, true); if (!res.isSuccess) { VLog.e(TAG, "Unable to install app %s: %s.", pkgName, res.error); FileUtils.deleteDir(appDir); } } isBooting = false; } @Override public InstallResult installApp(String apkPath, int flags) { return install(apkPath, flags, false); } private synchronized InstallResult install(String apkPath, int flags, boolean onlyScan) { if (apkPath == null) { return InstallResult.makeFailure("apk path = NULL"); } File apk = new File(apkPath); if (!apk.exists() || !apk.isFile()) { return InstallResult.makeFailure("APK File is not exist."); } PackageParser.Package pkg = null; PackageParser parser = null; try { Pair<PackageParser, PackageParser.Package> parseResult = PackageParserCompat.parsePackage(apk, 0); if (parseResult != null) { parser = parseResult.first; pkg = parseResult.second; } } catch (Throwable e) { e.printStackTrace(); } if (parser == null || pkg == null || pkg.packageName == null) { return InstallResult.makeFailure("Unable to parse the package."); } InstallResult res = new InstallResult(); res.packageName = pkg.packageName; // PackageCache holds all packages, try to check if we need to update. PackageParser.Package existOne = PackageCache.get(pkg.packageName); AppSetting existSetting = findAppInfo(pkg.packageName); if (existOne != null) { if ((flags & InstallStrategy.IGNORE_NEW_VERSION) != 0) { res.isUpdate = true; return res; } if (!canUpdate(existOne, pkg, flags)) { return InstallResult.makeFailure("Not allowed to update the package."); } res.isUpdate = true; } File appDir = VEnvironment.getDataAppPackageDirectory(pkg.packageName); File libDir = new File(appDir, "lib"); if (res.isUpdate) { FileUtils.deleteDir(libDir); VEnvironment.getOdexFile(pkg.packageName).delete(); VActivityManagerService.get().killAppByPkg(pkg.packageName, VUserHandle.USER_ALL); } if (!libDir.exists() && !libDir.mkdirs()) { return InstallResult.makeFailure("Unable to create lib dir."); } boolean dependSystem = (flags & InstallStrategy.DEPEND_SYSTEM_IF_EXIST) != 0 && VirtualCore.get().isOutsideInstalled(pkg.packageName); if (existSetting != null && existSetting.dependSystem) { dependSystem = false; } if (!onlyScan) { NativeLibraryHelperCompat.copyNativeBinaries(new File(apkPath), libDir); if (!dependSystem) { File storeFile = new File(appDir, "base.apk"); File parentFolder = storeFile.getParentFile(); if (!parentFolder.exists() && !parentFolder.mkdirs()) { VLog.w(TAG, "Warning: unable to create folder : " + storeFile.getPath()); } else if (storeFile.exists() && !storeFile.delete()) { VLog.w(TAG, "Warning: unable to delete file : " + storeFile.getPath()); } FileUtils.copyFile(apk, storeFile); apk = storeFile; } } if (existOne != null) { PackageCache.remove(pkg.packageName); } if (!dependSystem) { ResourcesUtils.chmod(appDir); } AppSetting appSetting = new AppSetting(); appSetting.parser = parser; appSetting.dependSystem = dependSystem; appSetting.apkPath = apk.getPath(); appSetting.libPath = libDir.getPath(); appSetting.packageName = pkg.packageName; appSetting.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg)); PackageCache.put(pkg, appSetting); BroadcastSystem.get().startApp(pkg); if (!onlyScan) { notifyAppInstalled(appSetting); } res.isSuccess = true; return res; } private boolean canUpdate(PackageParser.Package existOne, PackageParser.Package newOne, int flags) { if ((flags & InstallStrategy.COMPARE_VERSION) != 0) { if (existOne.mVersionCode < newOne.mVersionCode) { return true; } } if ((flags & InstallStrategy.TERMINATE_IF_EXIST) != 0) { return false; } if ((flags & InstallStrategy.UPDATE_IF_EXIST) != 0) { return true; } return false; } public boolean uninstallApp(String pkg) { synchronized (PackageCache.sPackageCaches) { AppSetting setting = findAppInfo(pkg); if (setting != null) { try { BroadcastSystem.get().stopApp(pkg); VActivityManagerService.get().killAppByPkg(pkg, VUserHandle.USER_ALL); FileUtils.deleteDir(VEnvironment.getDataAppPackageDirectory(pkg)); VEnvironment.getOdexFile(pkg).delete(); for (int userId : VUserManagerService.get().getUserIds()) { FileUtils.deleteDir(VEnvironment.getDataUserPackageDirectory(userId, pkg)); } PackageCache.remove(pkg); } catch (Exception e) { e.printStackTrace(); } finally { notifyAppUninstalled(setting); } return true; } } return false; } public List<AppSetting> getAllApps() { List<AppSetting> settings = new ArrayList<>(getAppCount()); for (PackageParser.Package p : PackageCache.sPackageCaches.values()) { settings.add((AppSetting) p.mExtras); } return settings; } public int getAppCount() { return PackageCache.sPackageCaches.size(); } public boolean isAppInstalled(String pkg) { return pkg != null && !"android".equals(pkg) && PackageCache.sPackageCaches.get(pkg) != null; } private void notifyAppInstalled(AppSetting setting) { int N = mRemoteCallbackList.beginBroadcast(); while (N-- > 0) { try { mRemoteCallbackList.getBroadcastItem(N).onNewApp(setting.packageName); } catch (RemoteException e) { // Ignore } } mRemoteCallbackList.finishBroadcast(); Intent virtualIntent = new Intent(Constants.ACTION_PACKAGE_ADDED); Uri uri = Uri.fromParts("package", setting.packageName, null); virtualIntent.setData(uri); for (int userId : VUserManagerService.get().getUserIds()) { Intent intent = new Intent(virtualIntent); intent.putExtra(Intent.EXTRA_UID, VUserHandle.getUid(userId, setting.appId)); VirtualCore.get().getContext().sendBroadcast(virtualIntent); } VAccountManagerService.get().refreshAuthenticatorCache(null); } private void notifyAppUninstalled(AppSetting setting) { int N = mRemoteCallbackList.beginBroadcast(); while (N-- > 0) { try { mRemoteCallbackList.getBroadcastItem(N).onRemoveApp(setting.packageName); } catch (RemoteException e) { // Ignore } } mRemoteCallbackList.finishBroadcast(); Intent virtualIntent = new Intent(Constants.ACTION_PACKAGE_REMOVED); Uri uri = Uri.fromParts("package", setting.packageName, null); virtualIntent.setData(uri); for (int userId : VUserManagerService.get().getUserIds()) { Intent intent = new Intent(virtualIntent); intent.putExtra(Intent.EXTRA_UID, VUserHandle.getUid(userId, setting.appId)); VirtualCore.get().getContext().sendBroadcast(virtualIntent); } VAccountManagerService.get().refreshAuthenticatorCache(null); } @Override public void registerObserver(IAppObserver observer) { try { mRemoteCallbackList.register(observer); } catch (Throwable e) { // Ignore } } @Override public void unregisterObserver(IAppObserver observer) { try { mRemoteCallbackList.unregister(observer); } catch (Throwable e) { // Ignore } } @Override public IAppRequestListener getAppRequestListener() { return listener; } @Override public void setAppRequestListener(final IAppRequestListener listener) { this.listener = listener; if (listener != null) { try { listener.asBinder().linkToDeath(new DeathRecipient() { @Override public void binderDied() { listener.asBinder().unlinkToDeath(this, 0); VAppManagerService.this.listener = null; } }, 0); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void clearAppRequestListener() { this.listener = null; } public AppSetting findAppInfo(String pkg) { synchronized (PackageCache.class) { if (pkg != null) { PackageParser.Package p = PackageCache.get(pkg); if (p != null) { return (AppSetting) p.mExtras; } } return null; } } public int getAppId(String pkg) { AppSetting setting = findAppInfo(pkg); return setting != null ? setting.appId : -1; } }