1 /* 2 * Copyright (C) 2017 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.development; 18 19 import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_MOCK_LOCATION_APP; 20 21 import android.Manifest; 22 import android.app.Activity; 23 import android.app.AppOpsManager; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.text.TextUtils; 29 30 import androidx.preference.Preference; 31 32 import com.android.settings.R; 33 import com.android.settings.core.PreferenceControllerMixin; 34 import com.android.settingslib.development.DeveloperOptionsPreferenceController; 35 36 import java.util.List; 37 38 public class MockLocationAppPreferenceController extends DeveloperOptionsPreferenceController 39 implements PreferenceControllerMixin, OnActivityResultListener { 40 41 private static final String MOCK_LOCATION_APP_KEY = "mock_location_app"; 42 private static final int[] MOCK_LOCATION_APP_OPS = new int[]{AppOpsManager.OP_MOCK_LOCATION}; 43 44 private final DevelopmentSettingsDashboardFragment mFragment; 45 private final AppOpsManager mAppsOpsManager; 46 private final PackageManager mPackageManager; 47 MockLocationAppPreferenceController(Context context, DevelopmentSettingsDashboardFragment fragment)48 public MockLocationAppPreferenceController(Context context, 49 DevelopmentSettingsDashboardFragment fragment) { 50 super(context); 51 52 mFragment = fragment; 53 mAppsOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 54 mPackageManager = context.getPackageManager(); 55 } 56 57 @Override getPreferenceKey()58 public String getPreferenceKey() { 59 return MOCK_LOCATION_APP_KEY; 60 } 61 62 @Override handlePreferenceTreeClick(Preference preference)63 public boolean handlePreferenceTreeClick(Preference preference) { 64 if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { 65 return false; 66 } 67 final Intent intent = new Intent(mContext, AppPicker.class); 68 intent.putExtra(AppPicker.EXTRA_REQUESTIING_PERMISSION, 69 Manifest.permission.ACCESS_MOCK_LOCATION); 70 mFragment.startActivityForResult(intent, REQUEST_MOCK_LOCATION_APP); 71 return true; 72 } 73 74 @Override updateState(Preference preference)75 public void updateState(Preference preference) { 76 updateMockLocation(); 77 } 78 79 @Override onActivityResult(int requestCode, int resultCode, Intent data)80 public boolean onActivityResult(int requestCode, int resultCode, Intent data) { 81 if (requestCode != REQUEST_MOCK_LOCATION_APP || resultCode != Activity.RESULT_OK) { 82 return false; 83 } 84 writeMockLocation(data.getAction()); 85 updateMockLocation(); 86 return true; 87 } 88 updateMockLocation()89 private void updateMockLocation() { 90 final String mockLocationApp = getCurrentMockLocationApp(); 91 92 if (!TextUtils.isEmpty(mockLocationApp)) { 93 mPreference.setSummary( 94 mContext.getResources().getString(R.string.mock_location_app_set, 95 getAppLabel(mockLocationApp))); 96 } else { 97 mPreference.setSummary( 98 mContext.getResources().getString(R.string.mock_location_app_not_set)); 99 } 100 } 101 writeMockLocation(String mockLocationAppName)102 private void writeMockLocation(String mockLocationAppName) { 103 removeAllMockLocations(); 104 // Enable the app op of the new mock location app if such. 105 if (!TextUtils.isEmpty(mockLocationAppName)) { 106 try { 107 final ApplicationInfo ai = mPackageManager.getApplicationInfo( 108 mockLocationAppName, PackageManager.MATCH_DISABLED_COMPONENTS); 109 mAppsOpsManager.setMode(AppOpsManager.OP_MOCK_LOCATION, ai.uid, 110 mockLocationAppName, AppOpsManager.MODE_ALLOWED); 111 } catch (PackageManager.NameNotFoundException e) { 112 /* ignore */ 113 } 114 } 115 } 116 getAppLabel(String mockLocationApp)117 private String getAppLabel(String mockLocationApp) { 118 try { 119 final ApplicationInfo ai = mPackageManager.getApplicationInfo( 120 mockLocationApp, PackageManager.MATCH_DISABLED_COMPONENTS); 121 final CharSequence appLabel = mPackageManager.getApplicationLabel(ai); 122 return appLabel != null ? appLabel.toString() : mockLocationApp; 123 } catch (PackageManager.NameNotFoundException e) { 124 return mockLocationApp; 125 } 126 } 127 removeAllMockLocations()128 private void removeAllMockLocations() { 129 // Disable the app op of the previous mock location app if such. 130 final List<AppOpsManager.PackageOps> packageOps = mAppsOpsManager.getPackagesForOps( 131 MOCK_LOCATION_APP_OPS); 132 if (packageOps == null) { 133 return; 134 } 135 // Should be one but in case we are in a bad state due to use of command line tools. 136 for (AppOpsManager.PackageOps packageOp : packageOps) { 137 if (packageOp.getOps().get(0).getMode() != AppOpsManager.MODE_ERRORED) { 138 removeMockLocationForApp(packageOp.getPackageName()); 139 } 140 } 141 } 142 removeMockLocationForApp(String appName)143 private void removeMockLocationForApp(String appName) { 144 try { 145 final ApplicationInfo ai = mPackageManager.getApplicationInfo( 146 appName, PackageManager.MATCH_DISABLED_COMPONENTS); 147 mAppsOpsManager.setMode(AppOpsManager.OP_MOCK_LOCATION, ai.uid, 148 appName, AppOpsManager.MODE_ERRORED); 149 } catch (PackageManager.NameNotFoundException e) { 150 /* ignore */ 151 } 152 } 153 getCurrentMockLocationApp()154 private String getCurrentMockLocationApp() { 155 final List<AppOpsManager.PackageOps> packageOps = mAppsOpsManager.getPackagesForOps( 156 MOCK_LOCATION_APP_OPS); 157 if (packageOps != null) { 158 for (AppOpsManager.PackageOps packageOp : packageOps) { 159 if (packageOp.getOps().get(0).getMode() == AppOpsManager.MODE_ALLOWED) { 160 return packageOps.get(0).getPackageName(); 161 } 162 } 163 } 164 return null; 165 } 166 } 167