• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Manifest;
22 import android.app.Activity;
23 import android.app.ActivityManager;
24 import android.app.Dialog;
25 import android.app.admin.DevicePolicyManager;
26 import android.app.settings.SettingsEnums;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.hardware.usb.IUsbManager;
36 import android.os.Bundle;
37 import android.os.IBinder;
38 import android.os.RemoteException;
39 import android.os.ServiceManager;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.text.TextUtils;
43 import android.util.Log;
44 
45 import androidx.annotation.VisibleForTesting;
46 import androidx.appcompat.app.AlertDialog;
47 import androidx.fragment.app.DialogFragment;
48 import androidx.fragment.app.Fragment;
49 
50 import com.android.settings.SettingsActivity;
51 import com.android.settings.SettingsPreferenceFragment;
52 import com.android.settings.applications.manageapplications.ManageApplications;
53 import com.android.settings.core.SubSettingLauncher;
54 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
55 import com.android.settings.overlay.FeatureFactory;
56 import com.android.settingslib.RestrictedLockUtilsInternal;
57 import com.android.settingslib.applications.ApplicationsState;
58 import com.android.settingslib.applications.ApplicationsState.AppEntry;
59 
60 import java.util.ArrayList;
61 
62 public abstract class AppInfoBase extends SettingsPreferenceFragment
63         implements ApplicationsState.Callbacks {
64 
65     public static final String ARG_PACKAGE_NAME = "package";
66     public static final String ARG_PACKAGE_UID = "uid";
67 
68     private static final String TAG = "AppInfoBase";
69 
70     protected EnforcedAdmin mAppsControlDisallowedAdmin;
71     protected boolean mAppsControlDisallowedBySystem;
72 
73     protected ApplicationFeatureProvider mApplicationFeatureProvider;
74     protected ApplicationsState mState;
75     protected ApplicationsState.Session mSession;
76     protected ApplicationsState.AppEntry mAppEntry;
77     protected PackageInfo mPackageInfo;
78     protected int mUserId;
79     protected String mPackageName;
80 
81     protected IUsbManager mUsbManager;
82     protected DevicePolicyManager mDpm;
83     protected UserManager mUserManager;
84     protected PackageManager mPm;
85 
86     // Dialog identifiers used in showDialog
87     protected static final int DLG_BASE = 0;
88 
89     protected boolean mFinishing;
90     protected boolean mListeningToPackageRemove;
91 
92     @Override
onCreate(Bundle savedInstanceState)93     public void onCreate(Bundle savedInstanceState) {
94         super.onCreate(savedInstanceState);
95         mFinishing = false;
96         final Activity activity = getActivity();
97         mApplicationFeatureProvider = FeatureFactory.getFeatureFactory()
98                 .getApplicationFeatureProvider();
99         mState = ApplicationsState.getInstance(activity.getApplication());
100         mSession = mState.newSession(this, getSettingsLifecycle());
101         mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
102         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
103         mPm = activity.getPackageManager();
104         IBinder b = ServiceManager.getService(Context.USB_SERVICE);
105         mUsbManager = IUsbManager.Stub.asInterface(b);
106 
107         retrieveAppEntry();
108         startListeningToPackageRemove();
109     }
110 
111     @Override
onResume()112     public void onResume() {
113         super.onResume();
114         mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
115                 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId);
116         mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
117                 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId);
118 
119         if (!refreshUi()) {
120             setIntentAndFinish(true /* appChanged */);
121         }
122     }
123 
124 
125     @Override
onDestroy()126     public void onDestroy() {
127         stopListeningToPackageRemove();
128         super.onDestroy();
129     }
130 
retrieveAppEntry()131     protected String retrieveAppEntry() {
132         final Bundle args = getArguments();
133         mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
134         Intent intent = (args == null) ?
135                 getIntent() : (Intent) args.getParcelable("intent");
136         if (mPackageName == null) {
137             if (intent != null && intent.getData() != null) {
138                 mPackageName = intent.getData().getSchemeSpecificPart();
139             }
140         }
141         if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
142             mUserId = ((UserHandle) intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE))
143                     .getIdentifier();
144             if (mUserId != UserHandle.myUserId() && !hasInteractAcrossUsersFullPermission()) {
145                 Log.w(TAG, "Intent not valid.");
146                 finish();
147                 return "";
148             }
149         } else {
150             mUserId = UserHandle.myUserId();
151         }
152         mAppEntry = mState.getEntry(mPackageName, mUserId);
153         if (mAppEntry != null) {
154             // Get application info again to refresh changed properties of application
155             try {
156                 mPackageInfo = mPm.getPackageInfoAsUser(
157                         mAppEntry.info.packageName,
158                         PackageManager.PackageInfoFlags.of(
159                                 PackageManager.MATCH_DISABLED_COMPONENTS
160                                         | PackageManager.GET_SIGNING_CERTIFICATES
161                                         | PackageManager.GET_PERMISSIONS
162                                         | PackageManager.MATCH_ARCHIVED_PACKAGES),
163                         mUserId);
164             } catch (NameNotFoundException e) {
165                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
166             }
167         } else {
168             Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
169             mPackageInfo = null;
170         }
171 
172         return mPackageName;
173     }
174 
175     @VisibleForTesting
hasInteractAcrossUsersFullPermission()176     protected boolean hasInteractAcrossUsersFullPermission() {
177         Activity activity = getActivity();
178         if (!(activity instanceof SettingsActivity)) {
179             return false;
180         }
181         try {
182             int callerUid = ActivityManager.getService().getLaunchedFromUid(
183                     activity.getActivityToken());
184             if (ActivityManager.checkUidPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
185                     callerUid) != PackageManager.PERMISSION_GRANTED) {
186                 Log.w(TAG, "Uid " + callerUid + " does not have required permission "
187                         + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
188                 return false;
189             }
190             return true;
191         } catch (RemoteException e) {
192             return false;
193         }
194     }
195 
setIntentAndFinish(boolean appChanged)196     protected void setIntentAndFinish(boolean appChanged) {
197         Log.i(TAG, "appChanged=" + appChanged);
198         Intent intent = new Intent();
199         intent.putExtra(ManageApplications.APP_CHG, appChanged);
200         SettingsActivity sa = (SettingsActivity) getActivity();
201         sa.finishPreferencePanel(Activity.RESULT_OK, intent);
202         mFinishing = true;
203     }
204 
showDialogInner(int id, int moveErrorCode)205     protected void showDialogInner(int id, int moveErrorCode) {
206         DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
207         newFragment.setTargetFragment(this, 0);
208         newFragment.show(getFragmentManager(), "dialog " + id);
209     }
210 
refreshUi()211     protected abstract boolean refreshUi();
212 
createDialog(int id, int errorCode)213     protected abstract AlertDialog createDialog(int id, int errorCode);
214 
215     @Override
onRunningStateChanged(boolean running)216     public void onRunningStateChanged(boolean running) {
217         // No op.
218     }
219 
220     @Override
onRebuildComplete(ArrayList<AppEntry> apps)221     public void onRebuildComplete(ArrayList<AppEntry> apps) {
222         // No op.
223     }
224 
225     @Override
onPackageIconChanged()226     public void onPackageIconChanged() {
227         // No op.
228     }
229 
230     @Override
onPackageSizeChanged(String packageName)231     public void onPackageSizeChanged(String packageName) {
232         // No op.
233     }
234 
235     @Override
onAllSizesComputed()236     public void onAllSizesComputed() {
237         // No op.
238     }
239 
240     @Override
onLauncherInfoChanged()241     public void onLauncherInfoChanged() {
242         // No op.
243     }
244 
245     @Override
onLoadEntriesCompleted()246     public void onLoadEntriesCompleted() {
247         // No op.
248     }
249 
250     @Override
onPackageListChanged()251     public void onPackageListChanged() {
252         if (!refreshUi()) {
253             setIntentAndFinish(true /* appChanged */);
254         }
255     }
256 
startAppInfoFragment(Class<?> fragment, String title, String pkg, int uid, Fragment source, int request, int sourceMetricsCategory)257     public static void startAppInfoFragment(Class<?> fragment, String title,
258             String pkg, int uid, Fragment source, int request, int sourceMetricsCategory) {
259         final Bundle args = new Bundle();
260         args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg);
261         args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid);
262 
263         new SubSettingLauncher(source.getContext())
264                 .setDestination(fragment.getName())
265                 .setSourceMetricsCategory(sourceMetricsCategory)
266                 .setTitleText(title)
267                 .setArguments(args)
268                 .setUserHandle(new UserHandle(UserHandle.getUserId(uid)))
269                 .setResultListener(source, request)
270                 .launch();
271     }
272 
273     /** Starts app info fragment from SPA pages. */
startAppInfoFragment(Class<?> fragment, String title, ApplicationInfo app, Context context, int sourceMetricsCategory)274     public static void startAppInfoFragment(Class<?> fragment, String title, ApplicationInfo app,
275             Context context, int sourceMetricsCategory) {
276         final Bundle args = new Bundle();
277         args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.packageName);
278         args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.uid);
279 
280         new SubSettingLauncher(context)
281                 .setDestination(fragment.getName())
282                 .setSourceMetricsCategory(sourceMetricsCategory)
283                 .setTitleText(title)
284                 .setArguments(args)
285                 .setUserHandle(UserHandle.getUserHandleForUid(app.uid))
286                 .launch();
287     }
288 
289     public static class MyAlertDialogFragment extends InstrumentedDialogFragment {
290 
291         private static final String ARG_ID = "id";
292 
293         @Override
getMetricsCategory()294         public int getMetricsCategory() {
295             return SettingsEnums.DIALOG_BASE_APP_INFO_ACTION;
296         }
297 
298         @Override
onCreateDialog(Bundle savedInstanceState)299         public Dialog onCreateDialog(Bundle savedInstanceState) {
300             int id = getArguments().getInt(ARG_ID);
301             int errorCode = getArguments().getInt("moveError");
302             Dialog dialog = ((AppInfoBase) getTargetFragment()).createDialog(id, errorCode);
303             if (dialog == null) {
304                 throw new IllegalArgumentException("unknown id " + id);
305             }
306             return dialog;
307         }
308 
newInstance(int id, int errorCode)309         public static MyAlertDialogFragment newInstance(int id, int errorCode) {
310             MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment();
311             Bundle args = new Bundle();
312             args.putInt(ARG_ID, id);
313             args.putInt("moveError", errorCode);
314             dialogFragment.setArguments(args);
315             return dialogFragment;
316         }
317     }
318 
startListeningToPackageRemove()319     protected void startListeningToPackageRemove() {
320         if (mListeningToPackageRemove) {
321             return;
322         }
323         mListeningToPackageRemove = true;
324         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
325         filter.addDataScheme("package");
326         getContext().registerReceiver(mPackageRemovedReceiver, filter);
327     }
328 
stopListeningToPackageRemove()329     protected void stopListeningToPackageRemove() {
330         if (!mListeningToPackageRemove) {
331             return;
332         }
333         mListeningToPackageRemove = false;
334         getContext().unregisterReceiver(mPackageRemovedReceiver);
335     }
336 
onPackageRemoved()337     protected void onPackageRemoved() {
338         getActivity().finishAndRemoveTask();
339     }
340 
341     protected final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
342         @Override
343         public void onReceive(Context context, Intent intent) {
344             String packageName = intent.getData().getSchemeSpecificPart();
345             if (!mFinishing && (mAppEntry == null || mAppEntry.info == null
346                     || TextUtils.equals(mAppEntry.info.packageName, packageName))) {
347                 onPackageRemoved();
348             }
349         }
350     };
351 
352 }
353