1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications; 18 19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21 import android.app.Activity; 22 import android.app.Dialog; 23 import android.app.admin.DevicePolicyManager; 24 import android.app.settings.SettingsEnums; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.PackageManager.NameNotFoundException; 33 import android.hardware.usb.IUsbManager; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.os.ServiceManager; 37 import android.os.UserHandle; 38 import android.os.UserManager; 39 import android.text.TextUtils; 40 import android.util.Log; 41 42 import androidx.appcompat.app.AlertDialog; 43 import androidx.fragment.app.DialogFragment; 44 import androidx.fragment.app.Fragment; 45 46 import com.android.settings.SettingsActivity; 47 import com.android.settings.SettingsPreferenceFragment; 48 import com.android.settings.applications.manageapplications.ManageApplications; 49 import com.android.settings.core.SubSettingLauncher; 50 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 51 import com.android.settings.overlay.FeatureFactory; 52 import com.android.settingslib.RestrictedLockUtilsInternal; 53 import com.android.settingslib.applications.ApplicationsState; 54 import com.android.settingslib.applications.ApplicationsState.AppEntry; 55 56 import java.util.ArrayList; 57 58 public abstract class AppInfoBase extends SettingsPreferenceFragment 59 implements ApplicationsState.Callbacks { 60 61 public static final String ARG_PACKAGE_NAME = "package"; 62 public static final String ARG_PACKAGE_UID = "uid"; 63 64 private static final String TAG = "AppInfoBase"; 65 66 protected EnforcedAdmin mAppsControlDisallowedAdmin; 67 protected boolean mAppsControlDisallowedBySystem; 68 69 protected ApplicationFeatureProvider mApplicationFeatureProvider; 70 protected ApplicationsState mState; 71 protected ApplicationsState.Session mSession; 72 protected ApplicationsState.AppEntry mAppEntry; 73 protected PackageInfo mPackageInfo; 74 protected int mUserId; 75 protected String mPackageName; 76 77 protected IUsbManager mUsbManager; 78 protected DevicePolicyManager mDpm; 79 protected UserManager mUserManager; 80 protected PackageManager mPm; 81 82 // Dialog identifiers used in showDialog 83 protected static final int DLG_BASE = 0; 84 85 protected boolean mFinishing; 86 protected boolean mListeningToPackageRemove; 87 88 @Override onCreate(Bundle savedInstanceState)89 public void onCreate(Bundle savedInstanceState) { 90 super.onCreate(savedInstanceState); 91 mFinishing = false; 92 final Activity activity = getActivity(); 93 mApplicationFeatureProvider = FeatureFactory.getFeatureFactory() 94 .getApplicationFeatureProvider(); 95 mState = ApplicationsState.getInstance(activity.getApplication()); 96 mSession = mState.newSession(this, getSettingsLifecycle()); 97 mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 98 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 99 mPm = activity.getPackageManager(); 100 IBinder b = ServiceManager.getService(Context.USB_SERVICE); 101 mUsbManager = IUsbManager.Stub.asInterface(b); 102 103 retrieveAppEntry(); 104 startListeningToPackageRemove(); 105 } 106 107 @Override onResume()108 public void onResume() { 109 super.onResume(); 110 mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 111 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); 112 mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( 113 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); 114 115 if (!refreshUi()) { 116 setIntentAndFinish(true /* appChanged */); 117 } 118 } 119 120 121 @Override onDestroy()122 public void onDestroy() { 123 stopListeningToPackageRemove(); 124 super.onDestroy(); 125 } 126 retrieveAppEntry()127 protected String retrieveAppEntry() { 128 final Bundle args = getArguments(); 129 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 130 Intent intent = (args == null) ? 131 getIntent() : (Intent) args.getParcelable("intent"); 132 if (mPackageName == null) { 133 if (intent != null && intent.getData() != null) { 134 mPackageName = intent.getData().getSchemeSpecificPart(); 135 } 136 } 137 if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { 138 mUserId = ((UserHandle) intent.getParcelableExtra( 139 Intent.EXTRA_USER_HANDLE)).getIdentifier(); 140 } else { 141 mUserId = UserHandle.myUserId(); 142 } 143 mAppEntry = mState.getEntry(mPackageName, mUserId); 144 if (mAppEntry != null) { 145 // Get application info again to refresh changed properties of application 146 try { 147 mPackageInfo = mPm.getPackageInfoAsUser( 148 mAppEntry.info.packageName, 149 PackageManager.PackageInfoFlags.of( 150 PackageManager.MATCH_DISABLED_COMPONENTS 151 | PackageManager.GET_SIGNING_CERTIFICATES 152 | PackageManager.GET_PERMISSIONS 153 | PackageManager.MATCH_ARCHIVED_PACKAGES), 154 mUserId); 155 } catch (NameNotFoundException e) { 156 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 157 } 158 } else { 159 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 160 mPackageInfo = null; 161 } 162 163 return mPackageName; 164 } 165 setIntentAndFinish(boolean appChanged)166 protected void setIntentAndFinish(boolean appChanged) { 167 Log.i(TAG, "appChanged=" + appChanged); 168 Intent intent = new Intent(); 169 intent.putExtra(ManageApplications.APP_CHG, appChanged); 170 SettingsActivity sa = (SettingsActivity) getActivity(); 171 sa.finishPreferencePanel(Activity.RESULT_OK, intent); 172 mFinishing = true; 173 } 174 showDialogInner(int id, int moveErrorCode)175 protected void showDialogInner(int id, int moveErrorCode) { 176 DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); 177 newFragment.setTargetFragment(this, 0); 178 newFragment.show(getFragmentManager(), "dialog " + id); 179 } 180 refreshUi()181 protected abstract boolean refreshUi(); 182 createDialog(int id, int errorCode)183 protected abstract AlertDialog createDialog(int id, int errorCode); 184 185 @Override onRunningStateChanged(boolean running)186 public void onRunningStateChanged(boolean running) { 187 // No op. 188 } 189 190 @Override onRebuildComplete(ArrayList<AppEntry> apps)191 public void onRebuildComplete(ArrayList<AppEntry> apps) { 192 // No op. 193 } 194 195 @Override onPackageIconChanged()196 public void onPackageIconChanged() { 197 // No op. 198 } 199 200 @Override onPackageSizeChanged(String packageName)201 public void onPackageSizeChanged(String packageName) { 202 // No op. 203 } 204 205 @Override onAllSizesComputed()206 public void onAllSizesComputed() { 207 // No op. 208 } 209 210 @Override onLauncherInfoChanged()211 public void onLauncherInfoChanged() { 212 // No op. 213 } 214 215 @Override onLoadEntriesCompleted()216 public void onLoadEntriesCompleted() { 217 // No op. 218 } 219 220 @Override onPackageListChanged()221 public void onPackageListChanged() { 222 if (!refreshUi()) { 223 setIntentAndFinish(true /* appChanged */); 224 } 225 } 226 startAppInfoFragment(Class<?> fragment, String title, String pkg, int uid, Fragment source, int request, int sourceMetricsCategory)227 public static void startAppInfoFragment(Class<?> fragment, String title, 228 String pkg, int uid, Fragment source, int request, int sourceMetricsCategory) { 229 final Bundle args = new Bundle(); 230 args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg); 231 args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid); 232 233 new SubSettingLauncher(source.getContext()) 234 .setDestination(fragment.getName()) 235 .setSourceMetricsCategory(sourceMetricsCategory) 236 .setTitleText(title) 237 .setArguments(args) 238 .setUserHandle(new UserHandle(UserHandle.getUserId(uid))) 239 .setResultListener(source, request) 240 .launch(); 241 } 242 243 /** Starts app info fragment from SPA pages. */ startAppInfoFragment(Class<?> fragment, String title, ApplicationInfo app, Context context, int sourceMetricsCategory)244 public static void startAppInfoFragment(Class<?> fragment, String title, ApplicationInfo app, 245 Context context, int sourceMetricsCategory) { 246 final Bundle args = new Bundle(); 247 args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.packageName); 248 args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.uid); 249 250 new SubSettingLauncher(context) 251 .setDestination(fragment.getName()) 252 .setSourceMetricsCategory(sourceMetricsCategory) 253 .setTitleText(title) 254 .setArguments(args) 255 .setUserHandle(UserHandle.getUserHandleForUid(app.uid)) 256 .launch(); 257 } 258 259 public static class MyAlertDialogFragment extends InstrumentedDialogFragment { 260 261 private static final String ARG_ID = "id"; 262 263 @Override getMetricsCategory()264 public int getMetricsCategory() { 265 return SettingsEnums.DIALOG_BASE_APP_INFO_ACTION; 266 } 267 268 @Override onCreateDialog(Bundle savedInstanceState)269 public Dialog onCreateDialog(Bundle savedInstanceState) { 270 int id = getArguments().getInt(ARG_ID); 271 int errorCode = getArguments().getInt("moveError"); 272 Dialog dialog = ((AppInfoBase) getTargetFragment()).createDialog(id, errorCode); 273 if (dialog == null) { 274 throw new IllegalArgumentException("unknown id " + id); 275 } 276 return dialog; 277 } 278 newInstance(int id, int errorCode)279 public static MyAlertDialogFragment newInstance(int id, int errorCode) { 280 MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); 281 Bundle args = new Bundle(); 282 args.putInt(ARG_ID, id); 283 args.putInt("moveError", errorCode); 284 dialogFragment.setArguments(args); 285 return dialogFragment; 286 } 287 } 288 startListeningToPackageRemove()289 protected void startListeningToPackageRemove() { 290 if (mListeningToPackageRemove) { 291 return; 292 } 293 mListeningToPackageRemove = true; 294 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 295 filter.addDataScheme("package"); 296 getContext().registerReceiver(mPackageRemovedReceiver, filter); 297 } 298 stopListeningToPackageRemove()299 protected void stopListeningToPackageRemove() { 300 if (!mListeningToPackageRemove) { 301 return; 302 } 303 mListeningToPackageRemove = false; 304 getContext().unregisterReceiver(mPackageRemovedReceiver); 305 } 306 onPackageRemoved()307 protected void onPackageRemoved() { 308 getActivity().finishAndRemoveTask(); 309 } 310 311 protected final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 312 @Override 313 public void onReceive(Context context, Intent intent) { 314 String packageName = intent.getData().getSchemeSpecificPart(); 315 if (!mFinishing && (mAppEntry == null || mAppEntry.info == null 316 || TextUtils.equals(mAppEntry.info.packageName, packageName))) { 317 onPackageRemoved(); 318 } 319 } 320 }; 321 322 } 323