188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang/* 288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * Copyright (C) 2013 The Android Open Source Project 388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * 488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * Licensed under the Apache License, Version 2.0 (the "License"); 588afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * you may not use this file except in compliance with the License. 688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * You may obtain a copy of the License at 788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * 888afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * http://www.apache.org/licenses/LICENSE-2.0 988afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * 1088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * Unless required by applicable law or agreed to in writing, software 1188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * distributed under the License is distributed on an "AS IS" BASIS, 1288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * See the License for the specific language governing permissions and 1488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * limitations under the License. 1588afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang */ 1688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 1788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangpackage com.android.settings.location; 1888afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 19b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christieimport android.app.ActivityManager; 2088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport android.app.AppOpsManager; 2188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport android.content.Context; 2288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport android.content.pm.ApplicationInfo; 2388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport android.content.pm.PackageManager; 24b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tangimport android.graphics.drawable.Drawable; 25c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tangimport android.os.Bundle; 26bec860d6c465e0c9649f627624c7aaa5d934ad1fDavid Christieimport android.os.Process; 27b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christieimport android.os.UserHandle; 28c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tangimport android.preference.Preference; 29c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tangimport android.preference.PreferenceActivity; 3088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport android.util.Log; 3188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 3288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport com.android.settings.R; 33c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tangimport com.android.settings.applications.InstalledAppDetails; 3488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 35b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tangimport java.util.ArrayList; 3688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangimport java.util.List; 3788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 3888afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang/** 3988afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * Retrieves the information of applications which accessed location recently. 4088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang */ 4188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tangpublic class RecentLocationApps { 4288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang private static final String TAG = RecentLocationApps.class.getSimpleName(); 43bec860d6c465e0c9649f627624c7aaa5d934ad1fDavid Christie private static final String ANDROID_SYSTEM_PACKAGE_NAME = "android"; 4488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 4588afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000; 4688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 47c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang private final PreferenceActivity mActivity; 48c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang private final PackageManager mPackageManager; 4988afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 505cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang public RecentLocationApps(PreferenceActivity activity) { 51c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang mActivity = activity; 52c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang mPackageManager = activity.getPackageManager(); 53c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang } 54c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang 55c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang private class PackageEntryClickedListener 56c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang implements Preference.OnPreferenceClickListener { 57c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang private String mPackage; 58c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang 59c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang public PackageEntryClickedListener(String packageName) { 60c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang mPackage = packageName; 61c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang } 62c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang 63c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang @Override 64c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang public boolean onPreferenceClick(Preference preference) { 65c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang // start new fragment to display extended information 66c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang Bundle args = new Bundle(); 67c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mPackage); 68c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang mActivity.startPreferencePanel(InstalledAppDetails.class.getName(), args, 69c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang R.string.application_info_label, null, null, 0); 70c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang return true; 71c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang } 7288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 7388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 7408a4c33a409f7cc693527de138179f80bf07be9cLifu Tang private Preference createRecentLocationEntry( 75b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang Drawable icon, 76b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang CharSequence label, 77b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang boolean isHighBattery, 78b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang Preference.OnPreferenceClickListener listener) { 79332822e31ba09b4c5a2bc86439f7c05efcdf9cdcLifu Tang Preference pref = new Preference(mActivity); 8008a4c33a409f7cc693527de138179f80bf07be9cLifu Tang pref.setIcon(icon); 8108a4c33a409f7cc693527de138179f80bf07be9cLifu Tang pref.setTitle(label); 82b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang if (isHighBattery) { 8308a4c33a409f7cc693527de138179f80bf07be9cLifu Tang pref.setSummary(R.string.location_high_battery_use); 84b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang } else { 8508a4c33a409f7cc693527de138179f80bf07be9cLifu Tang pref.setSummary(R.string.location_low_battery_use); 86b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang } 8708a4c33a409f7cc693527de138179f80bf07be9cLifu Tang pref.setOnPreferenceClickListener(listener); 8808a4c33a409f7cc693527de138179f80bf07be9cLifu Tang return pref; 89b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang } 90b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang 91b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang /** 9288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * Fills a list of applications which queried location recently within 9388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang * specified time. 9488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang */ 9508a4c33a409f7cc693527de138179f80bf07be9cLifu Tang public List<Preference> getAppList() { 96b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang // Retrieve a location usage list from AppOps 9788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang AppOpsManager aoManager = 98c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang (AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE); 9988afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps( 10088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang new int[] { 10188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang AppOpsManager.OP_MONITOR_LOCATION, 102c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 10388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang }); 104b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang 105b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang // Process the AppOps list and generate a preference list. 10625518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang ArrayList<Preference> prefs = new ArrayList<Preference>(); 10788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang long now = System.currentTimeMillis(); 10888afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang for (AppOpsManager.PackageOps ops : appOps) { 109bec860d6c465e0c9649f627624c7aaa5d934ad1fDavid Christie // Don't show the Android System in the list - it's not actionable for the user. 110b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christie // Also don't show apps belonging to background users. 111b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christie int uid = ops.getUid(); 112b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christie boolean isAndroidOs = (uid == Process.SYSTEM_UID) 113b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christie && ANDROID_SYSTEM_PACKAGE_NAME.equals(ops.getPackageName()); 114b1e381be2175dc1855a3de456dacd549ab282b2bDavid Christie if (!isAndroidOs && ActivityManager.getCurrentUser() == UserHandle.getUserId(uid)) { 1155cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang Preference pref = getPreferenceFromOps(now, ops); 116bec860d6c465e0c9649f627624c7aaa5d934ad1fDavid Christie if (pref != null) { 117bec860d6c465e0c9649f627624c7aaa5d934ad1fDavid Christie prefs.add(pref); 118bec860d6c465e0c9649f627624c7aaa5d934ad1fDavid Christie } 119c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang } 120c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang } 121c3e9ac937a4ba948d47cbb83f6999af34fd00b65Lifu Tang 12225518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang return prefs; 12388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 12488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 125b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang /** 12608a4c33a409f7cc693527de138179f80bf07be9cLifu Tang * Creates a Preference entry for the given PackageOps. 127b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang * 128b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang * This method examines the time interval of the PackageOps first. If the PackageOps is older 129b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang * than the designated interval, this method ignores the PackageOps object and returns null. 1305cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang * When the PackageOps is fresh enough, this method returns a Preference pointing to the App 1315cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang * Info page for that package. 132b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang */ 1335cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang private Preference getPreferenceFromOps(long now, AppOpsManager.PackageOps ops) { 13488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang String packageName = ops.getPackageName(); 13588afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang List<AppOpsManager.OpEntry> entries = ops.getOps(); 13688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang boolean highBattery = false; 13788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang boolean normalBattery = false; 1388b4cdbebcfb53b57ac2afec1b1e3377d6a02bb21David Christie // Earliest time for a location request to end and still be shown in list. 1398b4cdbebcfb53b57ac2afec1b1e3377d6a02bb21David Christie long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS; 14088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang for (AppOpsManager.OpEntry entry : entries) { 1418b4cdbebcfb53b57ac2afec1b1e3377d6a02bb21David Christie if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) { 14288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang switch (entry.getOp()) { 14388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang case AppOpsManager.OP_MONITOR_LOCATION: 14488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang normalBattery = true; 14588afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang break; 14688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION: 14788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang highBattery = true; 14888afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang break; 14988afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang default: 15088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang break; 15188afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 15288afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 15388afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 15488afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 15588afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang if (!highBattery && !normalBattery) { 15688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang if (Log.isLoggable(TAG, Log.VERBOSE)) { 15788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang Log.v(TAG, packageName + " hadn't used location within the time interval."); 15888afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 159b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang return null; 160b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang } 161b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang 162b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang // The package is fresh enough, continue. 163b9ab196bb520fed9b23aa9a259fab08084646c2cLifu Tang 16408a4c33a409f7cc693527de138179f80bf07be9cLifu Tang Preference pref = null; 1655cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang try { 1665cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang ApplicationInfo appInfo = mPackageManager.getApplicationInfo( 1675cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang packageName, PackageManager.GET_META_DATA); 1685cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang // Multiple users can install the same package. Each user gets a different Uid for 1695cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang // the same package. 1705cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang // 1715cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang // Here we retrieve the Uid with package name, that will be the Uid for that package 1725cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang // associated with the current active user. If the Uid differs from the Uid in ops, 1735cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang // that means this entry belongs to another inactive user and we should ignore that. 1745cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang if (appInfo.uid == ops.getUid()) { 1755cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang pref = createRecentLocationEntry( 1765cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang mPackageManager.getApplicationIcon(appInfo), 1775cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang mPackageManager.getApplicationLabel(appInfo), 1785cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang highBattery, 1795cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang new PackageEntryClickedListener(packageName)); 1805cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang } else if (Log.isLoggable(TAG, Log.VERBOSE)) { 1815cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang Log.v(TAG, "package " + packageName + " with Uid " + ops.getUid() + 1825cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang " belongs to another inactive account, ignored."); 1835cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang } 1845cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang } catch (PackageManager.NameNotFoundException e) { 1855cbf971e19d3eb03f02799fc1e2263b0f6221da4Lifu Tang Log.wtf(TAG, "Package not found: " + packageName, e); 18688afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 18788afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang 18808a4c33a409f7cc693527de138179f80bf07be9cLifu Tang return pref; 18988afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang } 19088afb32fc6422139dcf8340aa88f3de9112a1485Lifu Tang} 191