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