Merge "Check for location access on TelephonyRegistry" am: 871202da2f
am: cf59665a75
Change-Id: I8eee8b62f0e41fa7346126d29266dfd555550028
This commit is contained in:
@@ -146,6 +146,7 @@ applications that come with the platform
|
||||
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
|
||||
<permission name="android.permission.MANAGE_USERS"/>
|
||||
<permission name="android.permission.MODIFY_PHONE_STATE"/>
|
||||
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
|
||||
<permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
|
||||
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
|
||||
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.os.UserHandle;
|
||||
import android.telephony.CellInfo;
|
||||
import android.telephony.CellLocation;
|
||||
import android.telephony.DisconnectCause;
|
||||
import android.telephony.LocationAccessPolicy;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.PreciseCallState;
|
||||
import android.telephony.PreciseDataConnectionState;
|
||||
@@ -93,7 +94,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
IPhoneStateListener callback;
|
||||
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
|
||||
|
||||
int callerUserId;
|
||||
int callerUid;
|
||||
int callerPid;
|
||||
|
||||
int events;
|
||||
|
||||
@@ -117,7 +119,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
+ " callback=" + callback
|
||||
+ " onSubscriptionsChangedListenererCallback="
|
||||
+ onSubscriptionsChangedListenerCallback
|
||||
+ " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
|
||||
+ " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
|
||||
+ " events=" + Integer.toHexString(events)
|
||||
+ " canReadPhoneState=" + canReadPhoneState + "}";
|
||||
}
|
||||
@@ -356,6 +358,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
public void addOnSubscriptionsChangedListener(String callingPackage,
|
||||
IOnSubscriptionsChangedListener callback) {
|
||||
int callerUserId = UserHandle.getCallingUserId();
|
||||
mContext.getSystemService(AppOpsManager.class)
|
||||
.checkPackage(Binder.getCallingUid(), callingPackage);
|
||||
if (VDBG) {
|
||||
log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
|
||||
+ " callerUserId=" + callerUserId + " callback=" + callback
|
||||
@@ -399,7 +403,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
|
||||
r.onSubscriptionsChangedListenerCallback = callback;
|
||||
r.callingPackage = callingPackage;
|
||||
r.callerUserId = callerUserId;
|
||||
r.callerUid = Binder.getCallingUid();
|
||||
r.callerPid = Binder.getCallingPid();
|
||||
r.events = 0;
|
||||
r.canReadPhoneState = true; // permission has been enforced above
|
||||
if (DBG) {
|
||||
@@ -470,6 +475,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
private void listen(String callingPackage, IPhoneStateListener callback, int events,
|
||||
boolean notifyNow, int subId) {
|
||||
int callerUserId = UserHandle.getCallingUserId();
|
||||
mContext.getSystemService(AppOpsManager.class)
|
||||
.checkPackage(Binder.getCallingUid(), callingPackage);
|
||||
if (VDBG) {
|
||||
log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
|
||||
+ " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
|
||||
@@ -514,7 +521,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
|
||||
r.callback = callback;
|
||||
r.callingPackage = callingPackage;
|
||||
r.callerUserId = callerUserId;
|
||||
r.callerUid = Binder.getCallingUid();
|
||||
r.callerPid = Binder.getCallingPid();
|
||||
boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
|
||||
| ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
|
||||
r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
|
||||
@@ -572,8 +580,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
try {
|
||||
if (DBG_LOC) log("listen: mCellLocation = "
|
||||
+ mCellLocation[phoneId]);
|
||||
r.callback.onCellLocationChanged(
|
||||
new Bundle(mCellLocation[phoneId]));
|
||||
if (checkLocationAccess(r)) {
|
||||
r.callback.onCellLocationChanged(
|
||||
new Bundle(mCellLocation[phoneId]));
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
remove(r.binder);
|
||||
}
|
||||
@@ -619,7 +629,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
try {
|
||||
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
|
||||
+ mCellInfo.get(phoneId));
|
||||
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
|
||||
if (checkLocationAccess(r)) {
|
||||
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
remove(r.binder);
|
||||
}
|
||||
@@ -979,7 +991,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
mCellInfo.set(phoneId, cellInfo);
|
||||
for (Record r : mRecords) {
|
||||
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
|
||||
idMatch(r.subId, subId, phoneId)) {
|
||||
idMatch(r.subId, subId, phoneId) &&
|
||||
checkLocationAccess(r)) {
|
||||
try {
|
||||
if (DBG_LOC) {
|
||||
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
|
||||
@@ -1262,7 +1275,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
mCellLocation[phoneId] = cellLocation;
|
||||
for (Record r : mRecords) {
|
||||
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
|
||||
idMatch(r.subId, subId, phoneId)) {
|
||||
idMatch(r.subId, subId, phoneId) &&
|
||||
checkLocationAccess(r)) {
|
||||
try {
|
||||
if (DBG_LOC) {
|
||||
log("notifyCellLocation: cellLocation=" + cellLocation
|
||||
@@ -1706,10 +1720,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
boolean valid = false;
|
||||
try {
|
||||
foregroundUser = ActivityManager.getCurrentUser();
|
||||
valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events);
|
||||
valid = UserHandle.getUserId(r.callerUid) == foregroundUser
|
||||
&& r.matchPhoneStateListenerEvent(events);
|
||||
if (DBG | DBG_LOC) {
|
||||
log("validateEventsAndUserLocked: valid=" + valid
|
||||
+ " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
|
||||
+ " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
|
||||
+ " r.events=" + r.events + " events=" + events);
|
||||
}
|
||||
} finally {
|
||||
@@ -1741,6 +1756,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkLocationAccess(Record r) {
|
||||
long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return LocationAccessPolicy.canAccessCellLocation(mContext,
|
||||
r.callingPackage, r.callerUid, r.callerPid);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPossibleMissNotify(Record r, int phoneId) {
|
||||
int events = r.events;
|
||||
|
||||
@@ -1788,7 +1813,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
|
||||
+ mCellInfo.get(phoneId));
|
||||
}
|
||||
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
|
||||
if (checkLocationAccess(r)) {
|
||||
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
mRemoveList.add(r.binder);
|
||||
}
|
||||
@@ -1836,7 +1863,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
try {
|
||||
if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
|
||||
+ mCellLocation[phoneId]);
|
||||
r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
|
||||
if (checkLocationAccess(r)) {
|
||||
r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
mRemoveList.add(r.binder);
|
||||
}
|
||||
|
||||
160
telephony/java/android/telephony/LocationAccessPolicy.java
Normal file
160
telephony/java/android/telephony/LocationAccessPolicy.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 android.telephony;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper for performing location access checks.
|
||||
* @hide
|
||||
*/
|
||||
public final class LocationAccessPolicy {
|
||||
/**
|
||||
* 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 pid The pid of the package
|
||||
* @return boolean true or false if permissions is granted
|
||||
*/
|
||||
public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
|
||||
int uid, int pid) throws SecurityException {
|
||||
Trace.beginSection("TelephonyLocationCheck");
|
||||
try {
|
||||
// Always allow the phone process to access location. This avoid breaking legacy code
|
||||
// that rely on public-facing APIs to access cell location, and it doesn't create a
|
||||
// info leak risk because the cell location is stored in the phone process anyway.
|
||||
if (uid == Process.PHONE_UID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Granting ACCESS_FINE_LOCATION to an app automatically grants it
|
||||
// ACCESS_COARSE_LOCATION.
|
||||
|
||||
if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
|
||||
PackageManager.PERMISSION_DENIED) {
|
||||
return false;
|
||||
}
|
||||
final int opCode = AppOpsManager.permissionToOpCode(
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION);
|
||||
if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
|
||||
.noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
|
||||
return false;
|
||||
}
|
||||
if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
|
||||
&& !isLegacyForeground(context, pkgName, uid)) {
|
||||
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);
|
||||
} finally {
|
||||
Trace.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
|
||||
int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
|
||||
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
|
||||
return locationMode != Settings.Secure.LOCATION_MODE_OFF
|
||||
&& locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
|
||||
}
|
||||
|
||||
private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
|
||||
int uid) {
|
||||
long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
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, int uid) {
|
||||
final ActivityManager am = context.getSystemService(ActivityManager.class);
|
||||
return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
|
||||
}
|
||||
|
||||
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) {
|
||||
long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
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;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -541,9 +541,9 @@ public class SubscriptionManager {
|
||||
* onSubscriptionsChanged overridden.
|
||||
*/
|
||||
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
|
||||
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
|
||||
String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
|
||||
if (DBG) {
|
||||
logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
|
||||
logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
|
||||
+ " listener=" + listener);
|
||||
}
|
||||
try {
|
||||
@@ -552,7 +552,7 @@ public class SubscriptionManager {
|
||||
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
|
||||
"telephony.registry"));
|
||||
if (tr != null) {
|
||||
tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
|
||||
tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// Should not happen
|
||||
|
||||
Reference in New Issue
Block a user