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