Move device serial behing a permission
Build serial is non-user resettable freely available deivice
identifier. It can be used by ad-netowrks to track the user
across apps which violates the user's privacy.
This change deprecates Build.SERIAL and adds a new Build.getSerial()
API which requires holding the read_phone_state permission.
The Build.SERIAL value is set to "undefined" for apps targeting
high enough SDK and for legacy app the value is still available.
bug:31402365
Change-Id: Iddd13430b2bd1d9ab4966e31038ecabdbdcec06d
diff --git a/Android.mk b/Android.mk
index 6831945..8977433 100644
--- a/Android.mk
+++ b/Android.mk
@@ -221,6 +221,7 @@
core/java/android/os/IBatteryPropertiesListener.aidl \
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
core/java/android/os/ICancellationSignal.aidl \
+ core/java/android/os/IDeviceIdentifiersPolicyService.aidl \
core/java/android/os/IDeviceIdleController.aidl \
core/java/android/os/IHardwarePropertiesManager.aidl \
core/java/android/os/IMaintenanceActivityListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index fc3c747..6a182f0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28370,6 +28370,7 @@
public class Build {
ctor public Build();
method public static java.lang.String getRadioVersion();
+ method public static java.lang.String getSerial();
field public static final java.lang.String BOARD;
field public static final java.lang.String BOOTLOADER;
field public static final java.lang.String BRAND;
@@ -28385,7 +28386,7 @@
field public static final java.lang.String MODEL;
field public static final java.lang.String PRODUCT;
field public static final deprecated java.lang.String RADIO;
- field public static final java.lang.String SERIAL;
+ field public static final deprecated java.lang.String SERIAL;
field public static final java.lang.String[] SUPPORTED_32_BIT_ABIS;
field public static final java.lang.String[] SUPPORTED_64_BIT_ABIS;
field public static final java.lang.String[] SUPPORTED_ABIS;
diff --git a/api/system-current.txt b/api/system-current.txt
index 8565e64..d5ec878 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30852,6 +30852,7 @@
public class Build {
ctor public Build();
method public static java.lang.String getRadioVersion();
+ method public static java.lang.String getSerial();
field public static final java.lang.String BOARD;
field public static final java.lang.String BOOTLOADER;
field public static final java.lang.String BRAND;
@@ -30868,7 +30869,7 @@
field public static final boolean PERMISSIONS_REVIEW_REQUIRED;
field public static final java.lang.String PRODUCT;
field public static final deprecated java.lang.String RADIO;
- field public static final java.lang.String SERIAL;
+ field public static final deprecated java.lang.String SERIAL;
field public static final java.lang.String[] SUPPORTED_32_BIT_ABIS;
field public static final java.lang.String[] SUPPORTED_64_BIT_ABIS;
field public static final java.lang.String[] SUPPORTED_ABIS;
diff --git a/api/test-current.txt b/api/test-current.txt
index 3fb0c69..d94e712 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28442,6 +28442,7 @@
public class Build {
ctor public Build();
method public static java.lang.String getRadioVersion();
+ method public static java.lang.String getSerial();
field public static final java.lang.String BOARD;
field public static final java.lang.String BOOTLOADER;
field public static final java.lang.String BRAND;
@@ -28457,7 +28458,7 @@
field public static final java.lang.String MODEL;
field public static final java.lang.String PRODUCT;
field public static final deprecated java.lang.String RADIO;
- field public static final java.lang.String SERIAL;
+ field public static final deprecated java.lang.String SERIAL;
field public static final java.lang.String[] SUPPORTED_32_BIT_ABIS;
field public static final java.lang.String[] SUPPORTED_64_BIT_ABIS;
field public static final java.lang.String[] SUPPORTED_ABIS;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4ca1af8..387a1f5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -128,6 +128,8 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.text.DateFormat;
import java.util.ArrayList;
@@ -517,6 +519,7 @@
boolean persistent;
Configuration config;
CompatibilityInfo compatInfo;
+ String buildSerial;
/** Initial values for {@link Profiler}. */
ProfilerInfo initProfilerInfo;
@@ -851,7 +854,8 @@
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) {
+ CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
+ String buildSerial) {
if (services != null) {
// Setup the service cache in the ServiceManager
@@ -876,6 +880,7 @@
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
+ data.buildSerial = buildSerial;
sendMessage(H.BIND_APPLICATION, data);
}
@@ -5182,6 +5187,18 @@
StrictMode.enableDeathOnFileUriExposure();
}
+ // We deprecated Build.SERIAL and only apps that target pre NMR1
+ // SDK can see it. Since access to the serial is now behind a
+ // permission we push down the value and here we fix it up
+ // before any app code has been loaded.
+ try {
+ Field field = Build.class.getDeclaredField("SERIAL");
+ field.setAccessible(true);
+ field.set(Build.class, data.buildSerial);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ /* ignore */
+ }
+
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 3063d98..e41c841 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -301,10 +301,11 @@
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
HashMap<String, IBinder> services = data.readHashMap(null);
Bundle coreSettings = data.readBundle();
+ String buildSerial = data.readString();
bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
testWatcher, uiAutomationConnection, testMode, enableBinderTracking,
trackAllocation, restrictedBackupMode, persistent, config, compatInfo, services,
- coreSettings);
+ coreSettings, buildSerial);
return true;
}
@@ -1058,7 +1059,8 @@
IUiAutomationConnection uiAutomationConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode,
boolean persistent, Configuration config, CompatibilityInfo compatInfo,
- Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
+ Map<String, IBinder> services, Bundle coreSettings, String buildSerial)
+ throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(packageName);
@@ -1088,6 +1090,7 @@
compatInfo.writeToParcel(data, 0);
data.writeMap(services);
data.writeBundle(coreSettings);
+ data.writeString(buildSerial);
mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 7732157..ac8b1c4 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -98,8 +98,8 @@
IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
int debugMode, boolean enableBinderTracking, boolean trackAllocation,
boolean restrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings)
- throws RemoteException;
+ CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
+ String buildSerial) throws RemoteException;
void scheduleExit() throws RemoteException;
void scheduleSuicide() throws RemoteException;
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b295919..457866b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3675,6 +3675,12 @@
public static final String GATEKEEPER_SERVICE = "android.service.gatekeeper.IGateKeeperService";
/**
+ * Service defining the policy for access to device identifiers.
+ * @hide
+ */
+ public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 5b51002..0191589 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -16,7 +16,10 @@
package android.os;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.Context;
import android.text.TextUtils;
import android.util.Slog;
@@ -98,8 +101,35 @@
*/
public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
- /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
- public static final String SERIAL = getString("ro.serialno");
+ /**
+ * A hardware serial number, if available. Alphanumeric only, case-insensitive.
+ * For apps targeting SDK higher than {@link Build.VERSION_CODES#N_MR1} this
+ * field is set to {@link Build#UNKNOWN}.
+ *
+ * @deprecated Use {@link #getSerial()} instead.
+ **/
+ @Deprecated
+ // IMPORTANT: This field should be initialized via a function call to
+ // prevent its value being inlined in the app during compilation because
+ // we will later set it to the value based on the app's target SDK.
+ public static final String SERIAL = getString("no.such.thing");
+
+ /**
+ * Gets the hardware serial, if available. Requires holding the {@link
+ * android.Manifest.permission#READ_PHONE_STATE} permission.
+ * @return The serial if specified.
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public static String getSerial() {
+ IDeviceIdentifiersPolicyService service = IDeviceIdentifiersPolicyService.Stub
+ .asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE));
+ try {
+ return service.getSerial();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return UNKNOWN;
+ }
/**
* An ordered list of ABIs supported by this device. The most preferred ABI is the first
diff --git a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
new file mode 100644
index 0000000..ac19f2b
--- /dev/null
+++ b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.os;
+
+/**
+ * @hide
+ */
+interface IDeviceIdentifiersPolicyService {
+ String getSerial();
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 96706c0..ba4f041 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.os.IDeviceIdentifiersPolicyService;
import com.android.internal.telephony.TelephonyIntents;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -6516,6 +6517,17 @@
}
ProfilerInfo profilerInfo = profileFile == null ? null
: new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
+
+ // We deprecated Build.SERIAL and only apps that target pre NMR1
+ // SDK can see it. Since access to the serial is now behind a
+ // permission we push down the value.
+ String buildSerial = Build.UNKNOWN;
+ if (appInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+ buildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
+ .getSerial();
+ }
+
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode,
@@ -6523,7 +6535,9 @@
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
- mCoreSettingsObserver.getCoreSettingsLocked());
+ mCoreSettingsObserver.getCoreSettingsLocked(),
+ buildSerial);
+
updateLruProcessLocked(app, false, null);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
new file mode 100644
index 0000000..759030b
--- /dev/null
+++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 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.server.os;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IDeviceIdentifiersPolicyService;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import com.android.server.SystemService;
+
+/**
+ * This service defines the policy for accessing device identifiers.
+ */
+public final class DeviceIdentifiersPolicyService extends SystemService {
+ public DeviceIdentifiersPolicyService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.DEVICE_IDENTIFIERS_SERVICE,
+ new DeviceIdentifiersPolicy(getContext()));
+ }
+
+ private static final class DeviceIdentifiersPolicy
+ extends IDeviceIdentifiersPolicyService.Stub {
+ private final @NonNull Context mContext;
+
+ public DeviceIdentifiersPolicy(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public @Nullable String getSerial() throws RemoteException {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.READ_PHONE_STATE, "getSerial");
+ }
+ return SystemProperties.get("ro.serialno", Build.UNKNOWN);
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4591fcc..fb4ea30 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,7 +16,6 @@
package com.android.server;
-import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -32,7 +31,6 @@
import android.os.Environment;
import android.os.FactoryTest;
import android.os.FileUtils;
-import android.os.IPowerManager;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -79,6 +77,7 @@
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.notification.NotificationManagerService;
+import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.pm.BackgroundDexOptService;
import com.android.server.pm.Installer;
@@ -427,6 +426,12 @@
Installer installer = mSystemServiceManager.startService(Installer.class);
traceEnd();
+ // In some cases after launching an app we need to access device identifiers,
+ // therefore register the device identifier policy before the activity manager.
+ traceBeginAndSlog("DeviceIdentifiersPolicyService");
+ mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
+ traceEnd();
+
// Activity manager runs the show.
traceBeginAndSlog("StartActivityManager");
mActivityManagerService = mSystemServiceManager.startService(