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);
+ }
+}