Android_PackageInstall源码分析&静默安装
来源:互联网 发布:芜湖 网络推广专员 编辑:程序博客网 时间:2024/05/19 03:30
PackageInstall
阅读packageInstall源码我们的突破口是从程序的窗口开始,首先查看AndroidManifest文件中activity的定义
PackageInstallerActivity:
窗口指定了两个IntentFilter,因此两种方式调用该窗口,两种方式都定义了action: android.intent.action.INSTALL_PACKAGE,第一种方式需要在apk文件路径前加"file:"而且扩展名必须为apk,这个现实由android:mimeType决定,而第二种方式除了加"file:"前缀,还可以加"package:"前缀,对扩展名没有限制
UninstallerActivity:
只有一个IntentFilter,并且指定了两个action,在卸载应用程序时,指定哪一个action都可以,除此之外,还需要卸载程序的package,并且package必须加"package:"前缀
InstallAppProgress和UninstallerActivity没有定义任何action,因此无法被其他应用调用
这个四个activity都没有指定 android.intent.action.MAIN所以在程序列表中是没有图标的,PackageInstallerActivity和InstallAppProgress用于安装应用程序,UninstallerActivity和UninstallerActivity用于卸载应用程序(如果activity定义了action为MAIN,category为LAUNCHER,就会出现多个应用程序图标启动对应activity)
安装apk: 通过指定apk文件的绝对路径安装
Intent intent=new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(new File("/ascard/qq.apk")),"application/vnd.android.package-archive"); //满足第一个IntentFilter
// intent.setData(Uri.fromFile(new File("/sdcard/qq.apk"))); //满足第二个IntentFilter(没有指定MimeType)
startActivity(intent);
安装apk: 如果通过package方式安装,前提是该应用已经安装,也就是这种事方式是升级应用,如果未安装该应用,会弹出错误提示框
Intent intent=new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(Uri.parse("package:com.tencent.mobileqq"));
startActivity(intent);
卸载apk: 卸载apk只有一种方式,需要指定卸载应用的package,不过有两个可以设置的action
Intent intent=new Intent(Intent.ACTION_DELETE);
// Intent intent=new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.parse("package:com.tencent.mobileqq"));
startActivity(intent);
应用程序安装源码:
package com.android.packageinstaller;/* * This activity is launched when a new application is installed via side loading * The package is first parsed and the user is notified of parse errors via a dialog. * If the package is successfully parsed, the user is notified to turn on the install unknown * applications setting. A memory check is made at this point and the user is notified of out * of memory conditions if any. If the package is already existing on the device, * a confirmation dialog (to replace the existing package) is presented to the user. * Based on the user response the package is then installed by launching InstallAppConfirm * sub activity. All state transitions are handled in this activity */public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {private static final String TAG = "PackageInstaller";private int mSessionId = -1;private Uri mPackageURI; private Uri mOriginatingURI;private Uri mReferrerURI;private int mOriginatingUid = VerificationParams.NO_UID;private ManifestDigest mPkgDigest;private boolean localLOGV = false; PackageManager mPm; PackageInstaller mInstaller; PackageInfo mPkgInfo; ApplicationInfo mSourceInfo;// ApplicationInfo object primarily used for already existing applicationsprivate ApplicationInfo mAppInfo = null;private InstallFlowAnalytics mInstallFlowAnalytics;// View for install progressView mInstallConfirm;// Buttons to indicate user acceptanceprivate Button mOk;private Button mCancel; CaffeinatedScrollView mScrollView = null;private boolean mOkCanInstall = false;static final String PREFS_ALLOWED_SOURCES = "allowed_sources";private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";private static final String TAB_ID_ALL = "all";private static final String TAB_ID_NEW = "new";// Dialog identifiers used in showDialogprivate static final int DLG_BASE = 0;private static final int DLG_UNKNOWN_APPS = DLG_BASE + 1;private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;private void startInstallConfirm() { TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost); tabHost.setup(); ViewPager viewPager = (ViewPager)findViewById(R.id.pager); TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager); adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {@Overridepublic void onTabChanged(String tabId) {if (TAB_ID_ALL.equals(tabId)) {mInstallFlowAnalytics.setAllPermissionsDisplayed(true); } else if (TAB_ID_NEW.equals(tabId)) {mInstallFlowAnalytics.setNewPermissionsDisplayed(true); } } });boolean permVisible = false; // true: 显示权限列表 false: 未显示权限列表mScrollView = null;mOkCanInstall = false;int msg = 0;if (mPkgInfo != null) { AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);// 获取隐私相关权限的数量final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);// 获取与设备相关权限的数量final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);if (mAppInfo != null) {// 判断是否为系统应用,并使用不同的布局文件msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0? R.string.install_confirm_question_update_system : R.string.install_confirm_question_update;mScrollView = new CaffeinatedScrollView(this); //用来显示权限列表的滚动View // 如果显示的内容超过mScrollView,就会折叠可以滚动mScrollView.setFillViewport(true);boolean newPermissionsFound = (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);// 针对更新应用程序相对旧版本而已判断是否加入了新的权限if (newPermissionsFound) { permVisible = true;// 将新的权限列表视频添加到滚动视图中mScrollView.addView(perms.getPermissionsView( AppSecurityPermissions.WHICH_NEW)); } else { // 没有设置任何权限,只显示应用程序名称和图标LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); TextView label = (TextView)inflater.inflate(R.layout.label, null); label.setText(R.string.no_new_perms);mScrollView.addView(label); } adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator( getText(R.string.newPerms)), mScrollView); } else { // 无法获取应用的任何信息,将相应的控件隐藏findViewById(R.id.tabscontainer).setVisibility(View.GONE); findViewById(R.id.divider).setVisibility(View.VISIBLE); }// 如果至少设置了一个权限if (NP > 0 || ND > 0) { permVisible = true; LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); View root = inflater.inflate(R.layout.permissions_list, null);if (mScrollView == null) {mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview); }// 有与隐私相关的权限if (NP > 0) {// 在滚动视图上显示与隐私相关的权限((ViewGroup)root.findViewById(R.id.privacylist)).addView( perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL)); } else { root.findViewById(R.id.privacylist).setVisibility(View.GONE); }// 有与设备相关的权限if (ND > 0) {// 在滚动视图上显示与设备相关的权限((ViewGroup)root.findViewById(R.id.devicelist)).addView( perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE)); } else { root.findViewById(R.id.devicelist).setVisibility(View.GONE); } adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator( getText(R.string.allPerms)), root); } }mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);// 没有任何权限if (!permVisible) {if (mAppInfo != null) {// This is an update to an application, but there are no // permissions at all.msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0? R.string.install_confirm_question_update_system_no_perms : R.string.install_confirm_question_update_no_perms; } else {// This is a new application with no permissions.msg = R.string.install_confirm_question_no_perms; } tabHost.setVisibility(View.GONE);mInstallFlowAnalytics.setAllPermissionsDisplayed(false);mInstallFlowAnalytics.setNewPermissionsDisplayed(false); findViewById(R.id.filler).setVisibility(View.VISIBLE); findViewById(R.id.divider).setVisibility(View.GONE);mScrollView = null; }if (msg != 0) { ((TextView)findViewById(R.id.install_confirm_question)).setText(msg); }// mInstallConfirm为显示权限列表的最顶层视图mInstallConfirm.setVisibility(View.VISIBLE);mOk = (Button)findViewById(R.id.ok_button);mCancel = (Button)findViewById(R.id.cancel_button);mOk.setOnClickListener(this);mCancel.setOnClickListener(this);if (mScrollView == null) {// There is nothing to scroll view, so the ok button is immediately // set to install. // 应用程序没有任何权限,直接将按钮设为"安装"mOk.setText(R.string.install);mOkCanInstall = true; } else {// 显示权限列表是,当滚动到最后是,将按钮设置为"安装",否则是"下一步"mScrollView.setFullScrollAction(new Runnable() {@Overridepublic void run() {mOk.setText(R.string.install);mOkCanInstall = true; } }); } }private void showDialogInner(int id) {// TODO better fix for this? Remove dialog so that it gets created againremoveDialog(id); showDialog(id); }@Overridepublic Dialog onCreateDialog(int id, Bundle bundle) {switch (id) {case DLG_UNKNOWN_APPS:return new AlertDialog.Builder(this) .setTitle(R.string.unknown_apps_dlg_title) .setMessage(R.string.unknown_apps_dlg_text) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { Log.i(TAG, "Finishing off activity so that user can navigate to settings manually"); finish(); }}) .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { Log.i(TAG, "Launching settings"); launchSettingsAppAndFinish(); } }) .setOnCancelListener(this) .create(); case DLG_PACKAGE_ERROR :return new AlertDialog.Builder(this) .setTitle(R.string.Parse_error_dlg_title) .setMessage(R.string.Parse_error_dlg_text) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { finish(); } }) .setOnCancelListener(this) .create();case DLG_OUT_OF_SPACE:// Guaranteed not to be null. will default to package name if not set by appCharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo); String dlgText = getString(R.string.out_of_space_dlg_text, appTitle.toString());return new AlertDialog.Builder(this) .setTitle(R.string.out_of_space_dlg_title) .setMessage(dlgText) .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {//launch manage applicationsIntent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { Log.i(TAG, "Canceling installation"); finish(); } }) .setOnCancelListener(this) .create();case DLG_INSTALL_ERROR :// Guaranteed not to be null. will default to package name if not set by appCharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo); String dlgText1 = getString(R.string.install_failed_msg, appTitle1.toString());return new AlertDialog.Builder(this) .setTitle(R.string.install_failed) .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { finish(); } }) .setMessage(dlgText1) .setOnCancelListener(this) .create();case DLG_ALLOW_SOURCE: CharSequence appTitle2 = mPm.getApplicationLabel(mSourceInfo); String dlgText2 = getString(R.string.allow_source_dlg_text, appTitle2.toString());return new AlertDialog.Builder(this) .setTitle(R.string.allow_source_dlg_title) .setMessage(dlgText2) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { setResult(RESULT_CANCELED); finish(); }}) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES, Context.MODE_PRIVATE); prefs.edit().putBoolean(mSourceInfo.packageName, true).apply(); startInstallConfirm(); } }) .setOnCancelListener(this) .create(); }return null; }private void launchSettingsAppAndFinish() {// Create an intent to launch SettingsTwo activityIntent launchSettingsIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS); launchSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(launchSettingsIntent); finish(); }private boolean isInstallingUnknownAppsAllowed() { UserManager um = (UserManager) getSystemService(USER_SERVICE);boolean disallowedByUserManager = um.getUserRestrictions() .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, false);// 获取contentProvider中"未知来源"是否选中来判断 // 执行这句代码没有添加任何权限,但是前提是对apk进行系统签名,也就是说必须和源码一起编译或者使用mm/mmm命令单独进行编译 // 普通安装应用尽管编译能通过,但是执行到这里的时候回报错boolean allowedBySecureSettings = Settings.Secure.getInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;return (allowedBySecureSettings && (!disallowedByUserManager)); }private boolean isInstallRequestFromUnknownSource(Intent intent) { String callerPackage = getCallingPackage();if (callerPackage != null && intent.getBooleanExtra( Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {try {mSourceInfo = mPm.getApplicationInfo(callerPackage, 0);if (mSourceInfo != null) {if ((mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {// Privileged apps are not considered an unknown source.return false; } } } catch (NameNotFoundException e) { } }return true; }private boolean isVerifyAppsEnabled() { UserManager um = (UserManager) getSystemService(USER_SERVICE);if (um.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS)) {return true; }return Settings.Global.getInt(getContentResolver(), Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) > 0; }private boolean isAppVerifierInstalled() {final PackageManager pm = getPackageManager();final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.setType(PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0);return (receivers.size() > 0) ? true : false; }private void initiateInstall() { String pkgName = mPkgInfo.packageName;// Check if there is already a package on the device with this name // but it has been renamed to something else.String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });if (oldName != null && oldName.length > 0 && oldName[0] != null) { pkgName = oldName[0];mPkgInfo.packageName = pkgName;mPkgInfo.applicationInfo.packageName = pkgName; }// Check if package is already installed. display confirmation dialog if replacing pkgtry {// This is a little convoluted because we want to get all uninstalled // apps, but this may include apps with just data, and if it is just // data we still want to count it as "installed".mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.GET_UNINSTALLED_PACKAGES);if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {mAppInfo = null; } } catch (NameNotFoundException e) {mAppInfo = null; }mInstallFlowAnalytics.setReplace(mAppInfo != null);mInstallFlowAnalytics.setSystemApp( (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)); startInstallConfirm();// 显示一个确认对话框,安装的核心代码}void setPmResult(int pmResult) { Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, pmResult); setResult(pmResult == PackageManager.INSTALL_SUCCEEDED ? RESULT_OK : RESULT_FIRST_USER, result); }@Overrideprotected void onCreate(Bundle icicle) {super.onCreate(icicle);mPm = getPackageManager();mInstaller = mPm.getPackageInstaller();final Intent intent = getIntent();if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);if (info == null || !info.sealed || info.resolvedBaseCodePath == null) { Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring"); finish();return; }mSessionId = sessionId;mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));mOriginatingURI = null;mReferrerURI = null; } else {mSessionId = -1;mPackageURI = intent.getData();mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); }boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);mInstallFlowAnalytics = new InstallFlowAnalytics();mInstallFlowAnalytics.setContext(this);mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted( isInstallingUnknownAppsAllowed());mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());final String scheme = mPackageURI.getScheme();if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) { Log.w(TAG, "Unsupported scheme " + scheme); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME); finish();return; }final PackageUtil.AppSnippet as;// 处理scheme为package的情况if ("package".equals(mPackageURI.getScheme())) {mInstallFlowAnalytics.setFileUri(false);try {// 获取package对应的Android应用信息,如应用名称,权限列表..mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(), PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES); } catch (NameNotFoundException e) { }// 如果无法获取,则弹出一个错误提示框,然后退出安装if (mPkgInfo == null) { Log.w(TAG, "Requested package " + mPackageURI.getScheme() + " not available. Discontinuing installation"); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);mInstallFlowAnalytics.setPackageInfoObtained();mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);return; }// 创建AppSnippet对象,该对象封装了用于待安装Android应用的标题和图标as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),mPm.getApplicationIcon(mPkgInfo.applicationInfo)); } else { // 处理scheme为 file 的情况mInstallFlowAnalytics.setFileUri(true);// 获取apk文件的实际路径final File sourceFile = new File(mPackageURI.getPath());// 创建apk文件的分析器PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);// Check for parse errors // 如果无法创建文件分析器,则直接退出,弹出对话框if (parsed == null) { Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation"); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);mInstallFlowAnalytics.setPackageInfoObtained();mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);return; }// 获取apk文件的相关信息mPkgInfo = PackageParser.generatePackageInfo(parsed, null, PackageManager.GET_PERMISSIONS, 0, 0, null,new PackageUserState());mPkgDigest = parsed.manifestDigest;// 另一种创建APPSnippet的方式,使用更好的方式提取应用程序名称和图标as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile); }mInstallFlowAnalytics.setPackageInfoObtained();// 设置activity的视图 //set viewsetContentView(R.layout.install_start);mInstallConfirm = findViewById(R.id.install_confirm_panel);mInstallConfirm.setVisibility(View.INVISIBLE);// 创建显示应用名称(TextView)和图标(ImageView)的控件PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);// 获取应用的ID 对于普通应用,该值没有什么作用mOriginatingUid = getOriginatingUid(intent);// 判断系统是否允许安装未知来源的应用 // Block the install attempt on the Unknown Sources setting if necessary.if ((requestFromUnknownSource) && (!isInstallingUnknownAppsAllowed())) {//ask user to enable setting first // 询问是否通过设置打开"未知来源"showDialogInner(DLG_UNKNOWN_APPS);mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);return; }// 初始化安装initiateInstall(); }/** Get the ApplicationInfo for the calling package, if available */private ApplicationInfo getSourceInfo() { String callingPackage = getCallingPackage();if (callingPackage != null) {try {return mPm.getApplicationInfo(callingPackage, 0); } catch (NameNotFoundException ex) {// ignore} }return null; }/** Get the originating uid if possible, or VerificationParams.NO_UID if not available */private int getOriginatingUid(Intent intent) {// The originating uid from the intent. We only trust/use this if it comes from a // system applicationint uidFromIntent = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID, VerificationParams.NO_UID);// Get the source info from the calling package, if available. This will be the // definitive calling package, but it only works if the intent was started using // startActivityForResult,ApplicationInfo sourceInfo = getSourceInfo();if (sourceInfo != null) {if (uidFromIntent != VerificationParams.NO_UID && (mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {return uidFromIntent; }// We either didn't get a uid in the intent, or we don't trust it. Use the // uid of the calling package instead.return sourceInfo.uid; }// We couldn't get the specific calling package. Let's get the uid insteadint callingUid;try { callingUid = ActivityManagerNative.getDefault() .getLaunchedFromUid(getActivityToken()); } catch (android.os.RemoteException ex) { Log.w(TAG, "Could not determine the launching uid.");// nothing else we can doreturn VerificationParams.NO_UID; }// If we got a uid from the intent, we need to verify that the caller is a // privileged system package before we use itif (uidFromIntent != VerificationParams.NO_UID) { String[] callingPackages = mPm.getPackagesForUid(callingUid);if (callingPackages != null) {for (String packageName: callingPackages) {try { ApplicationInfo applicationInfo =mPm.getApplicationInfo(packageName, 0);if ((applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {return uidFromIntent; } } catch (NameNotFoundException ex) {// ignore it, and try the next package} } } }// We either didn't get a uid from the intent, or we don't trust it. Use the // calling uid instead.return callingUid; }@Overridepublic void onBackPressed() {if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, false); }mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);super.onBackPressed(); }// Generic handling when pressing back keypublic void onCancel(DialogInterface dialog) { finish(); }public void onClick(View v) {if(v == mOk) {if (mOkCanInstall || mScrollView == null) {mInstallFlowAnalytics.setInstallButtonClicked();if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, true);// We're only confirming permissions, so we don't really know how the // story ends; assume success.mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult( PackageManager.INSTALL_SUCCEEDED); } else {// Start subactivity to actually install the application // 执行安装事件时,首先会给Intent传递很多值给InstallAppProgress,对于普通的应用程序很多只都是null,Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,mPkgInfo.applicationInfo);// 设置要安装的URInewIntent.setData(mPackageURI);// 指定安装activitynewIntent.setClass(this, InstallAppProgress.class); newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest); newIntent.putExtra( InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME);if (mOriginatingURI != null) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); }if (mReferrerURI != null) { newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI); }if (mOriginatingUid != VerificationParams.NO_UID) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid); }if (installerPackageName != null) { newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName); }if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); }if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); } finish(); } else { // 点击"下一步"继续显示权限列表mScrollView.pageScroll(View.FOCUS_DOWN); } } else if(v == mCancel) { // 点击"取消"退出安装 // Cancel and finishsetResult(RESULT_CANCELED);if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, false); }mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_CANCELLED_BY_USER); finish(); } }}
startInstallConfirm方法主要是通过getPermissionsView方法获取不同权限的View,并在滚动视图中显示这些view
AppSecurityPermissions.WHICH_NEW 新加入的权限
AppSecurityPermissions.WHICH_PERSONAL 与用户隐私相关的权限
AppSecurityPermissions.WHICH_DEVICE 与设备相关的权限
package com.android.packageinstaller;/** * This activity corresponds to a download progress screen that is displayed * when the user tries * to install an application bundled as an apk file. The result of the application install * is indicated in the result code that gets set to the corresponding installation status * codes defined in PackageManager. If the package being installed already exists, * the existing package is replaced with the new one. */public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener {private final String TAG="InstallAppProgress";private boolean localLOGV = false;static final String EXTRA_MANIFEST_DIGEST ="com.android.packageinstaller.extras.manifest_digest";static final String EXTRA_INSTALL_FLOW_ANALYTICS ="com.android.packageinstaller.extras.install_flow_analytics";private ApplicationInfo mAppInfo;private Uri mPackageURI;private InstallFlowAnalytics mInstallFlowAnalytics;private ProgressBar mProgressBar;private View mOkPanel;private TextView mStatusTextView;private TextView mExplanationTextView;private Button mDoneButton;private Button mLaunchButton;private final int INSTALL_COMPLETE = 1;private Intent mLaunchIntent;private static final int DLG_OUT_OF_SPACE = 1;private CharSequence mLabel;private Handler mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case INSTALL_COMPLETE: //成功安装mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(msg.arg1);if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { Intent result = new Intent();result.putExtra(Intent.EXTRA_INSTALL_RESULT, msg.arg1); setResult(msg.arg1 == PackageManager.INSTALL_SUCCEEDED ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,result); finish();return; }// Update the status textmProgressBar.setVisibility(View.INVISIBLE);// Show the ok buttonint centerTextLabel;int centerExplanationLabel = -1; LevelListDrawable centerTextDrawable = (LevelListDrawable) getResources() .getDrawable(R.drawable.ic_result_status);if (msg.arg1 == PackageManager.INSTALL_SUCCEEDED) {mLaunchButton.setVisibility(View.VISIBLE); centerTextDrawable.setLevel(0); centerTextLabel = R.string.install_done;// Enable or disable launch buttonmLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppInfo.packageName);boolean enabled = false;if(mLaunchIntent != null) { List<ResolveInfo> list = getPackageManager(). queryIntentActivities(mLaunchIntent, 0);if (list != null && list.size() > 0) { enabled = true; } }if (enabled) {// 该按钮用于打开已经安装成功的程序mLaunchButton.setOnClickListener(InstallAppProgress.this); } else {mLaunchButton.setEnabled(false); }// 由于空间不足导致安装失败} else if (msg.arg1 == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE){ showDialogInner(DLG_OUT_OF_SPACE);return; } else { // 由于其他原因导致安装失败 // Generic error handling for all other error codes.centerTextDrawable.setLevel(1); centerExplanationLabel = getExplanationFromErrorCode(msg.arg1); centerTextLabel = R.string.install_failed;mLaunchButton.setVisibility(View.INVISIBLE); }if (centerTextDrawable != null) { centerTextDrawable.setBounds(0, 0, centerTextDrawable.getIntrinsicWidth(), centerTextDrawable.getIntrinsicHeight());mStatusTextView.setCompoundDrawablesRelative(centerTextDrawable, null,null, null); }mStatusTextView.setText(centerTextLabel);if (centerExplanationLabel != -1) {mExplanationTextView.setText(centerExplanationLabel);mExplanationTextView.setVisibility(View.VISIBLE); } else {mExplanationTextView.setVisibility(View.GONE); }mDoneButton.setOnClickListener(InstallAppProgress.this);mOkPanel.setVisibility(View.VISIBLE);break;default:break; } } };private int getExplanationFromErrorCode(int errCode) { Log.d(TAG, "Installation error code: " + errCode);switch (errCode) {case PackageManager.INSTALL_FAILED_INVALID_APK:return R.string.install_failed_invalid_apk;case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:return R.string.install_failed_inconsistent_certificates;case PackageManager.INSTALL_FAILED_OLDER_SDK:return R.string.install_failed_older_sdk;case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE:return R.string.install_failed_cpu_abi_incompatible;default:return -1; } }@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle); Intent intent = getIntent();// 获取待安装应用相关的值mAppInfo和mPackageURImAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);mInstallFlowAnalytics.setContext(this);mPackageURI = intent.getData();// 验证scheme是否为file或packagefinal String scheme = mPackageURI.getScheme();if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {mInstallFlowAnalytics.setFlowFinished( InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);throw new IllegalArgumentException("unexpected scheme " + scheme); }// 安装应用的核心代码initView(); }@Overridepublic Dialog onCreateDialog(int id, Bundle bundle) {switch (id) {case DLG_OUT_OF_SPACE: String dlgText = getString(R.string.out_of_space_dlg_text, mLabel);return new AlertDialog.Builder(this) .setTitle(R.string.out_of_space_dlg_title) .setMessage(dlgText) .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {//launch manage applicationsIntent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE"); startActivity(intent); finish(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) { Log.i(TAG, "Canceling installation"); finish(); } }) .setOnCancelListener(this) .create(); }return null; }private void showDialogInner(int id) { removeDialog(id); showDialog(id); }class PackageInstallObserver extends IPackageInstallObserver.Stub {public void packageInstalled(String packageName, int returnCode) {// 该方法会在其他线程中调用,因此通过handler更新UIMessage msg = mHandler.obtainMessage(INSTALL_COMPLETE); msg.arg1 = returnCode;mHandler.sendMessage(msg); } }public void initView() { setContentView(R.layout.op_progress);int installFlags = 0; PackageManager pm = getPackageManager();try {// 如果待安装的程序已经安装,返回PackageInfo对象,否则返回nullPackageInfo pi = pm.getPackageInfo(mAppInfo.packageName, PackageManager.GET_UNINSTALLED_PACKAGES);if(pi != null) {// 如果应用已经安装,设置安装模式为更新installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; } } catch (NameNotFoundException e) { }if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) { Log.w(TAG, "Replacing package:" + mAppInfo.packageName); }final PackageUtil.AppSnippet as;// 如果scheme为package,意味着更新程序if ("package".equals(mPackageURI.getScheme())) { as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo), pm.getApplicationIcon(mAppInfo)); } else { // 通过apk文件路径获取显示应用相关信息的视图final File sourceFile = new File(mPackageURI.getPath()); as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile); }mLabel = as.label; PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);mStatusTextView = (TextView)findViewById(R.id.center_text);mStatusTextView.setText(R.string.installing);mExplanationTextView = (TextView) findViewById(R.id.center_explanation);mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);mProgressBar.setIndeterminate(true);// Hide button till progress is being displayedmOkPanel = (View)findViewById(R.id.buttons_panel);mDoneButton = (Button)findViewById(R.id.done_button);mLaunchButton = (Button)findViewById(R.id.launch_button);mOkPanel.setVisibility(View.INVISIBLE);// 获取待安装应用的package nameString installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME);// 对于普通的Android应用没下吗连个uri为nullUri originatingURI = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); Uri referrer = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);int originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, VerificationParams.NO_UID); ManifestDigest manifestDigest = getIntent().getParcelableExtra(EXTRA_MANIFEST_DIGEST); VerificationParams verificationParams = new VerificationParams(null, originatingURI, referrer, originatingUid, manifestDigest);// 安装成功,失败的监听PackageInstallObserver observer = new PackageInstallObserver();// 核心逻辑if ("package".equals(mPackageURI.getScheme())) {try {//根据package name更新应用pm.installExistingPackage(mAppInfo.packageName); observer.packageInstalled(mAppInfo.packageName, PackageManager.INSTALL_SUCCEEDED); } catch (PackageManager.NameNotFoundException e) { observer.packageInstalled(mAppInfo.packageName, PackageManager.INSTALL_FAILED_INVALID_APK); } } else {// 安装或更新应用pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null); } }@Overrideprotected void onDestroy() {super.onDestroy(); }public void onClick(View v) {if(v == mDoneButton) {if (mAppInfo.packageName != null) { Log.i(TAG, "Finished installing "+mAppInfo.packageName); } finish(); } else if(v == mLaunchButton) { startActivity(mLaunchIntent); finish(); } }public void onCancel(DialogInterface dialog) { finish(); }}
PackageManager的installExistingPackage和installPackageWithVerificationAndEncryption都是静默安装,安装过程不会出现任何提示,不过这两个方法被设为hide,不能在普通Android应用中调用
查看源码发现安装过程中弹出的校验窗口是PackageInstaller有意展示的,同时Android的静默安装时通过如上方法实现的
应用程序卸载源码:
package com.android.packageinstaller;public class UninstallerActivity extends Activity {private static final String TAG = "UninstallerActivity";public static class UninstallAlertDialogFragment extends DialogFragment implementsDialogInterface.OnClickListener {@Overridepublic Dialog onCreateDialog(Bundle savedInstanceState) {final PackageManager pm = getActivity().getPackageManager();final DialogInfo dialogInfo = ((UninstallerActivity) getActivity()).mDialogInfo;final CharSequence appLabel = dialogInfo.appInfo.loadLabel(pm); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); StringBuilder messageBuilder = new StringBuilder();// If the Activity label differs from the App label, then make sure the user // knows the Activity belongs to the App being uninstalled.if (dialogInfo.activityInfo != null) {final CharSequence activityLabel = dialogInfo.activityInfo.loadLabel(pm);if (!activityLabel.equals(appLabel)) { messageBuilder.append( getString(R.string.uninstall_activity_text, activityLabel)); messageBuilder.append(" ").append(appLabel).append(".\n\n"); } }final boolean isUpdate = ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);if (isUpdate) { messageBuilder.append(getString(R.string.uninstall_update_text)); } else { UserManager userManager = UserManager.get(getActivity());if (dialogInfo.allUsers && userManager.getUserCount() >= 2) { messageBuilder.append(getString(R.string.uninstall_application_text_all_users)); } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) { UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier()); messageBuilder.append( getString(R.string.uninstall_application_text_user, userInfo.name)); } else { messageBuilder.append(getString(R.string.uninstall_application_text)); } } dialogBuilder.setTitle(appLabel); dialogBuilder.setIcon(dialogInfo.appInfo.loadIcon(pm)); dialogBuilder.setPositiveButton(android.R.string.ok, this); dialogBuilder.setNegativeButton(android.R.string.cancel, this); dialogBuilder.setMessage(messageBuilder.toString());return dialogBuilder.create(); }@Overridepublic void onClick(DialogInterface dialog, int which) {if (which == Dialog.BUTTON_POSITIVE) {// 开始卸载应用((UninstallerActivity) getActivity()).startUninstallProgress(); } else { // 取消((UninstallerActivity) getActivity()).dispatchAborted(); } }@Overridepublic void onDismiss(DialogInterface dialog) {super.onDismiss(dialog); getActivity().finish(); } }public static class AppNotFoundDialogFragment extends DialogFragment {@Overridepublic Dialog onCreateDialog(Bundle savedInstanceState) {return new AlertDialog.Builder(getActivity()) .setTitle(R.string.app_not_found_dlg_title) .setMessage(R.string.app_not_found_dlg_text) .setNeutralButton(android.R.string.ok, null) .create(); }@Overridepublic void onDismiss(DialogInterface dialog) {super.onDismiss(dialog); ((UninstallerActivity) getActivity()).dispatchAborted(); getActivity().setResult(Activity.RESULT_FIRST_USER); getActivity().finish(); } }static class DialogInfo { ApplicationInfo appInfo; ActivityInfo activityInfo;boolean allUsers; UserHandle user; IBinder callback; }private DialogInfo mDialogInfo;@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);// Get intent information. // We expect an intent with URI of the form package://<packageName>#<className> // className is optional; if specified, it is the activity the user chose to uninstallfinal Intent intent = getIntent();final Uri packageUri = intent.getData();if (packageUri == null) { Log.e(TAG, "No package URI in intent"); showAppNotFound();return; }final String packageName = packageUri.getEncodedSchemeSpecificPart();if (packageName == null) { Log.e(TAG, "Invalid package name in URI: " + packageUri); showAppNotFound();return; }final IPackageManager pm = IPackageManager.Stub.asInterface( ServiceManager.getService("package"));mDialogInfo = new DialogInfo();mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);if (mDialogInfo.user == null) {mDialogInfo.user = android.os.Process.myUserHandle(); }mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);try {mDialogInfo.appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier()); } catch (RemoteException e) { Log.e(TAG, "Unable to get packageName. Package manager is dead?"); }if (mDialogInfo.appInfo == null) { Log.e(TAG, "Invalid packageName: " + packageName); showAppNotFound();return; }// The class name may have been specified (e.g. when deleting an app from all apps)final String className = packageUri.getFragment();if (className != null) {try {mDialogInfo.activityInfo = pm.getActivityInfo(new ComponentName(packageName, className), 0,mDialogInfo.user.getIdentifier()); } catch (RemoteException e) { Log.e(TAG, "Unable to get className. Package manager is dead?");// Continue as the ActivityInfo isn't critical.} } showConfirmationDialog(); }private void showConfirmationDialog() { showDialogFragment(new UninstallAlertDialogFragment()); }private void showAppNotFound() { showDialogFragment(new AppNotFoundDialogFragment()); }private void showDialogFragment(DialogFragment fragment) { FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment prev = getFragmentManager().findFragmentByTag("dialog");if (prev != null) { ft.remove(prev); } fragment.show(ft, "dialog"); }void startUninstallProgress() { Intent newIntent = new Intent(Intent.ACTION_VIEW);//newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);// 要求卸载该应用对于用户程序和数据newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers); newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);// appInfo封装了卸载程序的信息(ApplicationInfo对象)newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);// 允许卸载窗口返回是否卸载成功的标识if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); }// 指定卸载应用的activitynewIntent.setClass(this, UninstallAppProgress.class); startActivity(newIntent); }void dispatchAborted() {if (mDialogInfo != null && mDialogInfo.callback != null) {final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(mDialogInfo.callback);try { observer.onPackageDeleted(mDialogInfo.appInfo.packageName, PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"); } catch (RemoteException ignored) { } } }}package com.android.packageinstaller;public class UninstallAppProgress extends Activity implements OnClickListener {private final String TAG="UninstallAppProgress";private boolean localLOGV = false;private ApplicationInfo mAppInfo;private boolean mAllUsers;private UserHandle mUser;private IBinder mCallback;private TextView mStatusTextView;private Button mOkButton;private Button mDeviceManagerButton;private ProgressBar mProgressBar;private View mOkPanel;private volatile int mResultCode = -1;private static final int UNINSTALL_COMPLETE = 1;private Handler mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case UNINSTALL_COMPLETE: //完成了卸载mResultCode = msg.arg1;final String packageName = (String) msg.obj;if (mCallback != null) {final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub .asInterface(mCallback);try { observer.onPackageDeleted(mAppInfo.packageName, mResultCode, packageName); } catch (RemoteException ignored) { } finish();return; }// 返回卸载的状态if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, mResultCode);// 根据是否成功卸载返回对应的值setResult(mResultCode == PackageManager.DELETE_SUCCEEDED ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER, result); finish();return; }// 下面是处理不需要返回卸载状态的情况 // Update the status textfinal String statusText;switch (msg.arg1) {// 成功卸载case PackageManager.DELETE_SUCCEEDED: statusText = getString(R.string.uninstall_done);// Show a Toast and finish the activityContext ctx = getBaseContext(); Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show(); setResultAndFinish(mResultCode);return;// 由于安全策略的原因导致卸载失败case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: Log.d(TAG, "Uninstall failed because " + packageName + " is a device admin");mDeviceManagerButton.setVisibility(View.VISIBLE); statusText = getString(R.string.uninstall_failed_device_policy_manager);break;case PackageManager.DELETE_FAILED_OWNER_BLOCKED: UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); IPackageManager packageManager = IPackageManager.Stub.asInterface( ServiceManager.getService("package")); List<UserInfo> users = userManager.getUsers();int blockingUserId = UserHandle.USER_NULL;for (int i = 0; i < users.size(); ++i) {final UserInfo user = users.get(i);try {if (packageManager.getBlockUninstallForUser(packageName, user.id)) { blockingUserId = user.id;break; } } catch (RemoteException e) {// Shouldn't happen.Log.e(TAG, "Failed to talk to package manager", e); } }mDeviceManagerButton.setVisibility(View.VISIBLE);if (blockingUserId == UserHandle.USER_OWNER) { statusText = getString(R.string.uninstall_blocked_device_owner); } else if (blockingUserId == UserHandle.USER_NULL) { Log.d(TAG, "Uninstall failed for " + packageName + " with code "+ msg.arg1 + " no blocking user"); statusText = getString(R.string.uninstall_failed); } else { String userName = userManager.getUserInfo(blockingUserId).name; statusText = String.format( getString(R.string.uninstall_blocked_profile_owner), userName); }break;default: //其他原因导致卸载失败Log.d(TAG, "Uninstall failed for " + packageName + " with code "+ msg.arg1); statusText = getString(R.string.uninstall_failed);break; }mStatusTextView.setText(statusText);// Hide the progress bar; Show the ok buttonmProgressBar.setVisibility(View.INVISIBLE);mOkPanel.setVisibility(View.VISIBLE);break;default:break; } } };@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle); Intent intent = getIntent();// 获取ApplicationInfo对象mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);// 获取是否删除所有用户数据的标识mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);if (mAllUsers && UserHandle.myUserId() != UserHandle.USER_OWNER) {throw new SecurityException("Only owner user can request uninstall for all users"); }mUser = intent.getParcelableExtra(Intent.EXTRA_USER);if (mUser == null) {mUser = android.os.Process.myUserHandle(); } else { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); List<UserHandle> profiles = userManager.getUserProfiles();if (!profiles.contains(mUser)) {throw new SecurityException("User " + android.os.Process.myUserHandle() + " can't "+ "request uninstall for user " + mUser); } }mCallback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK); initView(); // 卸载的逻辑}// 卸载监听类class PackageDeleteObserver extends IPackageDeleteObserver.Stub {public void packageDeleted(String packageName, int returnCode) { Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE); msg.arg1 = returnCode; msg.obj = packageName;mHandler.sendMessage(msg); } }void setResultAndFinish(int retCode) { setResult(retCode); finish(); }public void initView() {// 获取待卸载程序 是否为被更新的系统应用(该标识只作为标题显示用)boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); setTitle(isUpdate ? R.string.uninstall_update_title : R.string.uninstall_application_title); setContentView(R.layout.uninstall_progress);// Initialize viewsView snippetView = findViewById(R.id.app_snippet); PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView);mStatusTextView = (TextView) findViewById(R.id.center_text);mStatusTextView.setText(R.string.uninstalling);mDeviceManagerButton = (Button) findViewById(R.id.device_manager_button);mDeviceManagerButton.setVisibility(View.GONE);// 当由于某些安全策略的原因卸载失败,会显示该按钮,点击进入设置activity来改变某些安全策略mDeviceManagerButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) { Intent intent = new Intent(); intent.setClassName("com.android.settings","com.android.settings.Settings$DeviceAdminSettingsActivity"); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); } });mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);mProgressBar.setIndeterminate(true);// Hide button till progress is being displayedmOkPanel = (View) findViewById(R.id.ok_panel);mOkButton = (Button) findViewById(R.id.ok_button);mOkButton.setOnClickListener(this);mOkPanel.setVisibility(View.INVISIBLE); IPackageManager packageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));// 创建卸载监听PackageDeleteObserver observer = new PackageDeleteObserver();try {// 核心代码,静默卸载packageManager.deletePackageAsUser(mAppInfo.packageName, observer,mUser.getIdentifier(),mAllUsers ? PackageManager.DELETE_ALL_USERS : 0); } catch (RemoteException e) {// Shouldn't happen.Log.e(TAG, "Failed to talk to package manager", e); } }public void onClick(View v) {if(v == mOkButton) { Log.i(TAG, "Finished uninstalling pkg: " + mAppInfo.packageName); setResultAndFinish(mResultCode); } }@Overridepublic boolean dispatchKeyEvent(KeyEvent ev) {if (ev.getKeyCode() == KeyEvent.KEYCODE_BACK) {if (mResultCode == -1) {// Ignore back key when installation is in progressreturn true; } else {// If installation is done, just set the result codesetResult(mResultCode); } }return super.dispatchKeyEvent(ev); }}如果要实现应用程序静默安装和卸载都需要在AndroidManifest文件中添加以下权限(该权限属于系统级别的权限,不能在普通的Android应用中使用)<users-permission android:name="android.permission.INSTALL_PACKAGES">必须对Android应用进行系统签名(platform签名),并且通过刷机或root权限安装到Android系统中
2 0
- Android_PackageInstall源码分析&静默安装
- PackageInstaller 5.0源码分析静默安装与静默卸载
- Android 获取Root权限之后的静默安装实现 代码示例分析&&源码下载
- Android 获取Root权限之后的静默安装实现 代码示例分析&&源码下载
- 【安卓静默安装手段分析】
- Android 静默安装与卸载分析
- APK的安装流程及PackageManagerService源码解析&静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- 静默安装
- Netty Http协议栈开发(客户端&服务端)
- activity的跳转带返回数据
- Linux操作系统面试题
- netbeans无法连接数据库
- iOS开发系列--音频播放、录音、视频播放、拍照、视频录制
- Android_PackageInstall源码分析&静默安装
- java面试题十 string字符串操作
- Http相关介绍
- 2、SecureCRT中文乱码问题
- Android_handler源码分析,及介绍
- Java数据结构与算法
- 初学Python-第五章练习题
- Java反射
- java面试题十一 基本数据类型