Merge changes from topic "fix-telephony-registry-location"

* changes:
  Add a manual testapp for TelephonyRegistry
  Move LocationAccessPolicy
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8ce0d52..d2ef077 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -179,6 +179,7 @@
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
     <application android:name="PhoneApp"
             android:persistent="true"
diff --git a/src/com/android/phone/LocationAccessPolicy.java b/src/com/android/phone/LocationAccessPolicy.java
deleted file mode 100644
index 6f2a5ec..0000000
--- a/src/com/android/phone/LocationAccessPolicy.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import java.util.List;
-
-/**
- * Helper for performing location access checks.
- */
-final class LocationAccessPolicy {
-
-    private LocationAccessPolicy() {
-        /* do nothing - hide ctor */
-    }
-
-    /**
-     * API to determine if the caller has permissions to get cell location.
-     *
-     * @param pkgName Package name of the application requesting access
-     * @param uid The uid of the package
-     * @param message Message to add to the exception if no location permission
-     * @return boolean true or false if permissions is granted
-     */
-    static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
-            int uid, String message) throws SecurityException {
-        context.getSystemService(AppOpsManager.class).checkPackage(uid, pkgName);
-        // We always require the location permission and also require the
-        // location mode to be on for non-legacy apps. Legacy apps are
-        // required to be in the foreground to at least mitigate the case
-        // where a legacy app the user is not using tracks their location.
-
-        // Grating ACCESS_FINE_LOCATION to an app automatically grants it ACCESS_COARSE_LOCATION.
-        context.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION, message);
-        final int opCode = AppOpsManager.permissionToOpCode(
-                Manifest.permission.ACCESS_COARSE_LOCATION);
-        if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
-                .noteOp(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
-            return false;
-        }
-        if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
-                && !isLegacyForeground(context, pkgName)) {
-            return false;
-        }
-        // If the user or profile is current, permission is granted.
-        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
-        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
-    }
-
-    private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
-        return Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId)
-                != Settings.Secure.LOCATION_MODE_OFF;
-    }
-
-    private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName) {
-        return isLegacyVersion(context, pkgName) && isForegroundApp(context, pkgName);
-    }
-
-    private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
-        try {
-            if (context.getPackageManager().getApplicationInfo(pkgName, 0)
-                    .targetSdkVersion <= Build.VERSION_CODES.O) {
-                return true;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // In case of exception, assume known app (more strict checking)
-            // Note: This case will never happen since checkPackage is
-            // called to verify validity before checking app's version.
-        }
-        return false;
-    }
-
-    private static boolean isForegroundApp(@NonNull Context context, @NonNull String pkgName) {
-        final ActivityManager am = context.getSystemService(ActivityManager.class);
-        final List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
-        if (!tasks.isEmpty()) {
-            return pkgName.equals(tasks.get(0).topActivity.getPackageName());
-        }
-        return false;
-    }
-
-    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
-        return context.checkCallingOrSelfPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    private static boolean isCurrentProfile(@NonNull Context context, int uid) {
-        final int currentUser = ActivityManager.getCurrentUser();
-        final int callingUserId = UserHandle.getUserId(uid);
-        if (callingUserId == currentUser) {
-            return true;
-        } else {
-            List<UserInfo> userProfiles = context.getSystemService(
-                    UserManager.class).getProfiles(currentUser);
-            for (UserInfo user: userProfiles) {
-                if (user.id == callingUserId) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 9d7605f..e1e4303 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -55,6 +55,7 @@
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
 import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.LocationAccessPolicy;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.NetworkScanRequest;
@@ -1651,8 +1652,10 @@
 
     @Override
     public Bundle getCellLocation(String callingPackage) {
+        mPhone.getContext().getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
-                callingPackage, Binder.getCallingUid(), "getCellLocation")) {
+                callingPackage, Binder.getCallingUid(),Binder.getCallingPid())) {
             return null;
         }
 
@@ -1719,8 +1722,10 @@
     @Override
     @SuppressWarnings("unchecked")
     public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
+        mPhone.getContext().getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
-                callingPackage, Binder.getCallingUid(), "getNeighboringCellInfo")) {
+                callingPackage, Binder.getCallingUid(), Binder.getCallingPid())) {
             return null;
         }
 
@@ -1747,8 +1752,10 @@
 
     @Override
     public List<CellInfo> getAllCellInfo(String callingPackage) {
+        mPhone.getContext().getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
-                callingPackage, Binder.getCallingUid(), "getAllCellInfo")) {
+                callingPackage, Binder.getCallingUid(), Binder.getCallingPid())) {
             return null;
         }
 
diff --git a/testapps/TelephonyRegistryTestApp/Android.mk b/testapps/TelephonyRegistryTestApp/Android.mk
new file mode 100644
index 0000000..ed1f2a3
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := TelephonyRegistryTestApp
+
+LOCAL_CERTIFICATE := platform
+LOCAL_MODULE_TAGS := tests
+#LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..5f19509
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.phone.testapps.telephonyregistry">
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <application android:label="TelephonyRegistryTestApp">
+        <activity
+            android:name=".TelephonyRegistryTestApp"
+            android:label="TelephonyRegistryTestApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
diff --git a/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml b/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..405efe2
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:id="@+id/events">
+        </LinearLayout>
+    </ScrollView>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/queryCellLocationButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_row="0"
+            android:layout_column="0"
+            android:text="@string/query_cell_location_button" />
+        <Button
+            android:id="@+id/registerButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_row="0"
+            android:layout_column="0"
+            android:text="@string/register_button" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml b/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..897b83e
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <string name="register_button">Register</string>
+    <string name="query_cell_location_button">Query</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
new file mode 100644
index 0000000..e423619
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.phone.testapps.telephonyregistry;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.telephony.CellInfo;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.util.SparseArray;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TelephonyRegistryTestApp extends Activity {
+    private TelephonyManager telephonyManager;
+    private NotificationManager notificationManager;
+    private int mSelectedEvents = 0;
+    private static final String NOTIFICATION_CHANNEL = "registryUpdate";
+
+    private static final SparseArray<String> EVENTS = new SparseArray<String>() {{
+        put(PhoneStateListener.LISTEN_SERVICE_STATE, "SERVICE_STATE");
+        put(PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR, "MESSAGE_WAITING_INDICATOR");
+        put(PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR, "CALL_FORWARDING_INDICATOR");
+        put(PhoneStateListener.LISTEN_CELL_LOCATION, "CELL_LOCATION");
+        put(PhoneStateListener.LISTEN_CALL_STATE, "CALL_STATE");
+        put(PhoneStateListener.LISTEN_DATA_CONNECTION_STATE, "DATA_CONNECTION_STATE");
+        put(PhoneStateListener.LISTEN_DATA_ACTIVITY, "DATA_ACTIVITY");
+        put(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS, "SIGNAL_STRENGTHS");
+        put(PhoneStateListener.LISTEN_OTASP_CHANGED, "OTASP_CHANGED");
+        put(PhoneStateListener.LISTEN_CELL_INFO, "CELL_INFO");
+        put(PhoneStateListener.LISTEN_PRECISE_CALL_STATE, "PRECISE_CALL_STATE");
+        put(PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
+                "PRECISE_DATA_CONNECTION_STATE");
+        put(PhoneStateListener.LISTEN_VOLTE_STATE, "VOLTE_STATE");
+        put(PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE, "CARRIER_NETWORK_CHANGE");
+        put(PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE, "VOICE_ACTIVATION_STATE");
+        put(PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE, "DATA_ACTIVATION_STATE");
+    }};
+
+    private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onCellLocationChanged(CellLocation location) {
+            notify("onCellLocationChanged", location);
+        }
+
+        @Override
+        public void onCellInfoChanged(List<CellInfo> cellInfo) {
+            notify("onCellInfoChanged", cellInfo);
+        }
+
+        private void notify(String method, Object data) {
+            Notification.Builder builder = new Notification.Builder(TelephonyRegistryTestApp.this,
+                    NOTIFICATION_CHANNEL);
+            Notification notification = builder.setSmallIcon(android.R.drawable.sym_def_app_icon)
+                    .setContentTitle("Registry update: " + method)
+                    .setContentText(data == null ? "null" : data.toString())
+                    .build();
+            notificationManager.notify(0, notification);
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        telephonyManager = getSystemService(TelephonyManager.class);
+
+        LinearLayout eventContainer = (LinearLayout) findViewById(R.id.events);
+        for (int i = 0; i < EVENTS.size(); i++) {
+            CheckBox box = new CheckBox(this);
+            box.setText(EVENTS.valueAt(i));
+            final int eventCode = EVENTS.keyAt(i);
+            box.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                if (buttonView.isChecked()) {
+                    mSelectedEvents |= eventCode;
+                } else {
+                    mSelectedEvents &= ~eventCode;
+                }
+            });
+            eventContainer.addView(box);
+        }
+
+        Button registerButton = (Button) findViewById(R.id.registerButton);
+        registerButton.setOnClickListener(v ->
+                telephonyManager.listen(phoneStateListener, mSelectedEvents));
+
+        Button queryCellLocationButton = findViewById(R.id.queryCellLocationButton);
+        queryCellLocationButton.setOnClickListener(v -> {
+            List<CellInfo> cellInfos = telephonyManager.getAllCellInfo();
+            String cellInfoText;
+            if (cellInfos == null || cellInfos.size() == 0) {
+                cellInfoText = "null";
+            } else {
+                cellInfoText = cellInfos.stream().map(CellInfo::toString)
+                        .collect(Collectors.joining(","));
+            }
+            Toast.makeText(TelephonyRegistryTestApp.this, "queryCellInfo: " + cellInfoText,
+                    Toast.LENGTH_SHORT).show();
+        });
+
+        notificationManager = getSystemService(NotificationManager.class);
+        NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL,
+                "Telephony Registry updates", NotificationManager.IMPORTANCE_HIGH);
+        notificationManager.createNotificationChannel(channel);
+    }
+}