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