diff options
Diffstat (limited to 'src/com/android/packageinstaller/PackageInstallerActivity.java')
-rw-r--r-- | src/com/android/packageinstaller/PackageInstallerActivity.java | 148 |
1 files changed, 109 insertions, 39 deletions
diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java index afe6e012..a08e792d 100644 --- a/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -32,11 +32,14 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; +import android.content.pm.ResolveInfo; import android.content.pm.VerificationParams; import android.net.Uri; import android.os.Bundle; +import android.os.SystemClock; import android.provider.Settings; import android.support.v4.view.ViewPager; +import android.util.EventLog; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -48,6 +51,8 @@ import android.widget.TabHost; import android.widget.TextView; import java.io.File; +import java.io.Serializable; +import java.util.List; /* * This activity is launched when a new application is installed via side loading @@ -75,6 +80,8 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen // ApplicationInfo object primarily used for already existing applications private ApplicationInfo mAppInfo = null; + private InstallFlowAnalytics mInstallFlowAnalytics; + // View for install progress View mInstallConfirm; // Buttons to indicate user acceptance @@ -85,6 +92,11 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen 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 showDialog private static final int DLG_BASE = 0; private static final int DLG_UNKNOWN_APPS = DLG_BASE + 1; @@ -98,6 +110,16 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen tabHost.setup(); ViewPager viewPager = (ViewPager)findViewById(R.id.pager); TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager); + adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() { + @Override + public 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; mScrollView = null; @@ -113,7 +135,10 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen : R.string.install_confirm_question_update; mScrollView = new CaffeinatedScrollView(this); mScrollView.setFillViewport(true); - if (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0) { + boolean newPermissionsFound = + (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0); + mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound); + if (newPermissionsFound) { permVisible = true; mScrollView.addView(perms.getPermissionsView( AppSecurityPermissions.WHICH_NEW)); @@ -124,7 +149,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen label.setText(R.string.no_new_perms); mScrollView.addView(label); } - adapter.addTab(tabHost.newTabSpec("new").setIndicator( + adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator( getText(R.string.newPerms)), mScrollView); } else { findViewById(R.id.tabscontainer).setVisibility(View.GONE); @@ -150,10 +175,11 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen } else { root.findViewById(R.id.devicelist).setVisibility(View.GONE); } - adapter.addTab(tabHost.newTabSpec("all").setIndicator( + 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 @@ -166,6 +192,8 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen 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; @@ -310,6 +338,39 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen Settings.Global.INSTALL_NON_MARKET_APPS, 0) > 0; } + 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() { + 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 @@ -333,6 +394,11 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen } catch (NameNotFoundException e) { mAppInfo = null; } + + mInstallFlowAnalytics.setReplace(mAppInfo != null); + mInstallFlowAnalytics.setSystemApp( + (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)); + startInstallConfirm(); } @@ -354,15 +420,28 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); mPm = getPackageManager(); + boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent); + mInstallFlowAnalytics = new InstallFlowAnalytics(); + mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime()); + mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted( + isInstallingUnknownAppsAllowed()); + mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource); + mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled()); + mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled()); + 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; if ("package".equals(mPackageURI.getScheme())) { + mInstallFlowAnalytics.setFileUri(false); try { mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(), PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES); @@ -373,11 +452,15 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen + " not available. Discontinuing installation"); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK); + mInstallFlowAnalytics.setPackageInfoObtained(); + mInstallFlowAnalytics.setFlowFinished( + InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING); return; } as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo), mPm.getApplicationIcon(mPkgInfo.applicationInfo)); } else { + mInstallFlowAnalytics.setFileUri(true); final File sourceFile = new File(mPackageURI.getPath()); PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile); @@ -386,6 +469,9 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen 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; } mPkgInfo = PackageParser.generatePackageInfo(parsed, null, @@ -394,6 +480,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen mPkgDigest = parsed.manifestDigest; as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile); } + mInstallFlowAnalytics.setPackageInfoObtained(); //set view setContentView(R.layout.install_start); @@ -403,41 +490,12 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen mOriginatingUid = getOriginatingUid(intent); - // Deal with install source. - 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_SYSTEM) != 0) { - // System apps don't need to be approved. - initiateInstall(); - return; - } - /* for now this is disabled, since the user would need to - * have enabled the global "unknown sources" setting in the - * first place in order to get here. - SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES, - Context.MODE_PRIVATE); - if (prefs.getBoolean(mSourceInfo.packageName, false)) { - // User has already allowed this one. - initiateInstall(); - return; - } - //ask user to enable setting first - showDialogInner(DLG_ALLOW_SOURCE); - return; - */ - } - } catch (NameNotFoundException e) { - } - } - - // Check unknown sources. - if (!isInstallingUnknownAppsAllowed()) { + // 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(); @@ -470,7 +528,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen ApplicationInfo sourceInfo = getSourceInfo(); if (sourceInfo != null) { if (uidFromIntent != VerificationParams.NO_UID && - (mSourceInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + (mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { return uidFromIntent; } @@ -491,7 +549,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen } // If we got a uid from the intent, we need to verify that the caller is a - // system package before we use it + // privileged system package before we use it if (uidFromIntent != VerificationParams.NO_UID) { String[] callingPackages = mPm.getPackagesForUid(callingUid); if (callingPackages != null) { @@ -500,7 +558,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen ApplicationInfo applicationInfo = mPm.getApplicationInfo(packageName, 0); - if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + if ((applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { return uidFromIntent; } } catch (NameNotFoundException ex) { @@ -514,6 +572,13 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen return callingUid; } + @Override + public void onBackPressed() { + mInstallFlowAnalytics.setFlowFinished( + InstallFlowAnalytics.RESULT_CANCELLED_BY_USER); + super.onBackPressed(); + } + // Generic handling when pressing back key public void onCancel(DialogInterface dialog) { finish(); @@ -523,12 +588,15 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen if(v == mOk) { if (mOkCanInstall || mScrollView == null) { // Start subactivity to actually install the application + mInstallFlowAnalytics.setInstallButtonClicked(); Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.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) { @@ -557,6 +625,8 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen } else if(v == mCancel) { // Cancel and finish setResult(RESULT_CANCELED); + mInstallFlowAnalytics.setFlowFinished( + InstallFlowAnalytics.RESULT_CANCELLED_BY_USER); finish(); } } |