summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--OWNERS1
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/java/android/content/pm/OWNERS1
-rw-r--r--core/java/android/os/image/DynamicSystemManager.java18
-rw-r--r--core/java/android/os/image/IDynamicSystemService.aidl4
-rw-r--r--core/java/android/provider/DeviceConfig.java18
-rw-r--r--core/java/android/provider/Settings.java47
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java15
-rw-r--r--core/res/res/layout/accessibility_enable_service_warning.xml (renamed from core/res/res/layout/accessibility_enable_service_encryption_warning.xml)8
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/tests/coretests/src/android/provider/DeviceConfigTest.java76
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/OWNERS4
-rw-r--r--native/android/libandroid_net.map.txt8
-rw-r--r--native/android/net.c4
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java7
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags1
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java93
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java2
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java11
-rw-r--r--services/core/java/com/android/server/EntropyMixer.java255
-rw-r--r--services/core/java/com/android/server/RandomBlock.java101
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/EntropyMixerTest.java106
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/OWNERS1
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java4
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl3
29 files changed, 530 insertions, 277 deletions
diff --git a/OWNERS b/OWNERS
index 47d8c83fea03..d4d19365f4d1 100644
--- a/OWNERS
+++ b/OWNERS
@@ -31,5 +31,6 @@ per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
per-file *.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file Android.mk = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
+per-file framework-jarjar-rules.txt = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a5b91f30c67..95fc7ec58e95 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8705,6 +8705,7 @@ package android.provider {
public final class DeviceConfig {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index 4e674f6ec9a8..087e61d70576 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -6,6 +6,7 @@ patb@google.com
per-file PackageParser.java = set noparent
per-file PackageParser.java = chiuwinson@google.com,patb@google.com,toddke@google.com
+per-file *Capability* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index e8e47857ecba..9610b16a312c 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -16,13 +16,16 @@
package android.os.image;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
+import android.gsi.IGsiService;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.Pair;
/**
* The DynamicSystemManager offers a mechanism to use a new system image temporarily. After the
@@ -138,17 +141,18 @@ public class DynamicSystemManager {
* @param name The DSU partition name
* @param size Size of the DSU image in bytes
* @param readOnly True if the partition is read only, e.g. system.
- * @return {@code true} if the call succeeds. {@code false} either the device does not contain
- * enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be
- * true.
+ * @return {@code Integer} an IGsiService.INSTALL_* status code. {@link Session} an installation
+ * session object if successful, otherwise {@code null}.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public Session createPartition(String name, long size, boolean readOnly) {
+ public @NonNull Pair<Integer, Session> createPartition(
+ String name, long size, boolean readOnly) {
try {
- if (mService.createPartition(name, size, readOnly)) {
- return new Session();
+ int status = mService.createPartition(name, size, readOnly);
+ if (status == IGsiService.INSTALL_OK) {
+ return new Pair<>(status, new Session());
} else {
- return null;
+ return new Pair<>(status, null);
}
} catch (RemoteException e) {
throw new RuntimeException(e.toString());
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 4e69952fac2f..755368a85c40 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -35,10 +35,10 @@ interface IDynamicSystemService
* @param name The DSU partition name
* @param size Size of the DSU image in bytes
* @param readOnly True if this partition is readOnly
- * @return true if the call succeeds
+ * @return IGsiService.INSTALL_* status code
*/
@EnforcePermission("MANAGE_DYNAMIC_SYSTEM")
- boolean createPartition(@utf8InCpp String name, long size, boolean readOnly);
+ int createPartition(@utf8InCpp String name, long size, boolean readOnly);
/**
* Complete the current partition installation.
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 2d402199e196..93c1c20f8252 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -815,7 +815,7 @@ public final class DeviceConfig {
}
/**
- * Create a new property with the the provided name and value in the provided namespace, or
+ * Create a new property with the provided name and value in the provided namespace, or
* update the value of such a property if it already exists. The same name can exist in multiple
* namespaces and might have different values in any or all namespaces.
* <p>
@@ -865,6 +865,22 @@ public final class DeviceConfig {
}
/**
+ * Delete a property with the provided name and value in the provided namespace
+ *
+ * @param namespace The namespace containing the property to create or update.
+ * @param name The name of the property to create or update.
+ * @return True if the property was deleted or it did not exist in the first place.
+ * False if the storage implementation throws errors.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ return Settings.Config.deleteString(contentResolver, namespace, name);
+ }
+
+ /**
* Reset properties to their default values by removing the underlying values.
* <p>
* The method accepts an optional namespace parameter. If provided, only properties set within
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4372f19e421a..b7fcc03ef374 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2829,6 +2829,7 @@ public final class Settings {
// for the fast path of retrieving settings.
private final String mCallGetCommand;
private final String mCallSetCommand;
+ private final String mCallDeleteCommand;
private final String mCallListCommand;
private final String mCallSetAllCommand;
@@ -2840,17 +2841,19 @@ public final class Settings {
private GenerationTracker mGenerationTracker;
<T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
- this(uri, getCommand, setCommand, null, null, providerHolder,
+ String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
+ Class<T> callerClass) {
+ this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
callerClass);
}
private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, String listCommand, String setAllCommand,
+ String setCommand, String deleteCommand, String listCommand, String setAllCommand,
ContentProviderHolder providerHolder, Class<T> callerClass) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
+ mCallDeleteCommand = deleteCommand;
mCallListCommand = listCommand;
mCallSetAllCommand = setAllCommand;
mProviderHolder = providerHolder;
@@ -2908,6 +2911,20 @@ public final class Settings {
}
}
+ public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+ IContentProvider cp = mProviderHolder.getProvider(cr);
+ cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
+ return false;
+ }
+ return true;
+ }
+
@UnsupportedAppUsage
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
// Check if the target settings key is readable. Reject if the caller is not system and
@@ -3370,6 +3387,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SYSTEM,
CALL_METHOD_PUT_SYSTEM,
+ CALL_METHOD_DELETE_SYSTEM,
sProviderHolder,
System.class);
@@ -5690,6 +5708,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE,
+ CALL_METHOD_DELETE_SECURE,
sProviderHolder,
Secure.class);
@@ -15159,6 +15178,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL,
+ CALL_METHOD_DELETE_GLOBAL,
sProviderHolder,
Global.class);
@@ -16391,6 +16411,7 @@ public final class Settings {
DeviceConfig.CONTENT_URI,
CALL_METHOD_GET_CONFIG,
CALL_METHOD_PUT_CONFIG,
+ CALL_METHOD_DELETE_CONFIG,
CALL_METHOD_LIST_CONFIG,
CALL_METHOD_SET_ALL_CONFIG,
sProviderHolder,
@@ -16500,6 +16521,26 @@ public final class Settings {
}
/**
+ * Delete a name/value pair from the database for the specified namespace.
+ *
+ * @param resolver to access the database with.
+ * @param namespace to delete the name/value pair from.
+ * @param name to delete.
+ * @return true if the value was deleted, false on database errors. If the name/value pair
+ * did not exist, return True.
+ *
+ * @see #resetToDefaults(ContentResolver, int, String)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull String name) {
+ return sNameValueCache.deleteStringForUser(resolver,
+ createCompositeName(namespace, name), resolver.getUserId());
+ }
+
+ /**
* Reset the values to their defaults.
* <p>
* The method accepts an optional prefix parameter. If provided, only pairs with a name that
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 4a20ad0f33f5..fc2c8cca9796 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -33,7 +33,6 @@ import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.text.BidiFormatter;
import android.view.LayoutInflater;
@@ -282,19 +281,7 @@ public final class AccessibilityTargetHelper {
Context.LAYOUT_INFLATER_SERVICE);
final View content = inflater.inflate(
- R.layout.accessibility_enable_service_encryption_warning, /* root= */ null);
-
- final TextView encryptionWarningView = (TextView) content.findViewById(
- R.id.accessibility_encryption_warning);
- if (StorageManager.isNonDefaultBlockEncrypted()) {
- final String text = context.getString(
- R.string.accessibility_enable_service_encryption_warning,
- getServiceName(context, target.getLabel()));
- encryptionWarningView.setText(text);
- encryptionWarningView.setVisibility(View.VISIBLE);
- } else {
- encryptionWarningView.setVisibility(View.GONE);
- }
+ R.layout.accessibility_enable_service_warning, /* root= */ null);
final ImageView dialogIcon = content.findViewById(
R.id.accessibility_permissionDialog_icon);
diff --git a/core/res/res/layout/accessibility_enable_service_encryption_warning.xml b/core/res/res/layout/accessibility_enable_service_warning.xml
index 400051660592..01ef10177c5a 100644
--- a/core/res/res/layout/accessibility_enable_service_encryption_warning.xml
+++ b/core/res/res/layout/accessibility_enable_service_warning.xml
@@ -54,14 +54,6 @@
android:fontFamily="google-sans-medium"/>
<TextView
- android:id="@+id/accessibility_encryption_warning"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="10dip"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-
- <TextView
android:id="@+id/accessibility_permissionDialog_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 76631926a52a..db0cc277c3af 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4687,13 +4687,6 @@
<xliff:g id="service" example="TalkBack">%1$s</xliff:g> to have full control of your
device?</string>
- <!-- Warning that the device data will not be encrypted with password or PIN if
- enabling an accessibility service and there is a secure lock setup. [CHAR LIMIT=NONE] -->
- <string name="accessibility_enable_service_encryption_warning">If you turn on
- <xliff:g id="service" example="TalkBack">%1$s</xliff:g>, your device won’t use your screen
- lock to enhance data encryption.
- </string>
-
<!-- Warning description that explains that it's appropriate for accessibility
services to have full control to help users with accessibility needs. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_warning_description">Full control is appropriate for apps
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bacd25d026d4..a296aa5fbae5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3414,14 +3414,12 @@
<java-symbol type="string" name="accessibility_edit_shortcut_menu_volume_title" />
<java-symbol type="string" name="accessibility_uncheck_legacy_item_warning" />
- <java-symbol type="layout" name="accessibility_enable_service_encryption_warning" />
+ <java-symbol type="layout" name="accessibility_enable_service_warning" />
<java-symbol type="id" name="accessibility_permissionDialog_icon" />
<java-symbol type="id" name="accessibility_permissionDialog_title" />
- <java-symbol type="id" name="accessibility_encryption_warning" />
<java-symbol type="id" name="accessibility_permission_enable_allow_button" />
<java-symbol type="id" name="accessibility_permission_enable_deny_button" />
<java-symbol type="string" name="accessibility_enable_service_title" />
- <java-symbol type="string" name="accessibility_enable_service_encryption_warning" />
<java-symbol type="layout" name="accessibility_shortcut_chooser_item" />
<java-symbol type="id" name="accessibility_shortcut_target_checkbox" />
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd39cdee4c32..8d4f29a2a506 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -827,4 +827,80 @@ public class DeviceConfigTest {
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
}
+ @Test
+ public void deleteProperty_nullNamespace() {
+ try {
+ DeviceConfig.deleteProperty(null, KEY);
+ Assert.fail("Null namespace should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deleteProperty_nullName() {
+ try {
+ DeviceConfig.deleteProperty(NAMESPACE, null);
+ Assert.fail("Null name should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deletePropertyString() {
+ final String value = "new_value";
+ final String default_value = "default";
+ DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyBoolean() {
+ final boolean value = true;
+ final boolean default_value = false;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyInt() {
+ final int value = 123;
+ final int default_value = 999;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyLong() {
+ final long value = 456789;
+ final long default_value = 123456;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyFloat() {
+ final float value = 456.789f;
+ final float default_value = 123.456f;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deleteProperty_empty() {
+ assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
+ assertThat(result).isNull();
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/widget/OWNERS b/core/tests/coretests/src/com/android/internal/widget/OWNERS
index b40fe240d80c..659a3d1a5c46 100644
--- a/core/tests/coretests/src/com/android/internal/widget/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/widget/OWNERS
@@ -1,3 +1,7 @@
+include /core/java/com/android/internal/widget/OWNERS
+
+# Notifications related
+per-file CachingIconViewTest.java = file:/services/core/java/com/android/server/notification/OWNERS
# LockSettings related
per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index 32fd734d61a0..e9acdae4576a 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -5,20 +5,20 @@
# which might be a few years old.
LIBANDROID_NET {
global:
- # These functions have been part of the NDK since API 24.
+ # These functions have been part of the LL-NDK since API 24.
android_getaddrinfofornetwork; # llndk
android_setsocknetwork; # llndk
android_setprocnetwork; # llndk
- # These functions have been part of the NDK since API 29.
+ # These functions have been part of the LL-NDK since API 29.
android_res_cancel; # llndk
android_res_nquery; # llndk
android_res_nresult; # llndk
android_res_nsend; # llndk
- # These functions have been part of the NDK since API 31.
+ # These functions have been part of the LL-NDK since API 31.
android_getprocnetwork; # llndk
android_setprocdns; # llndk
android_getprocdns; # llndk
- # These functions have been part of the NDK since API 33.
+ # These functions have been part of the LL-NDK since API 33.
android_tag_socket_with_uid; # llndk
android_tag_socket; # llndk
android_untag_socket; # llndk
diff --git a/native/android/net.c b/native/android/net.c
index d7c22e1a5741..74db1845080b 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -162,11 +162,11 @@ void android_res_cancel(int nsend_fd) {
resNetworkCancel(nsend_fd);
}
-int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) {
+int android_tag_socket_with_uid(int sockfd, uint32_t tag, uid_t uid) {
return tagSocket(sockfd, tag, uid);
}
-int android_tag_socket(int sockfd, int tag) {
+int android_tag_socket(int sockfd, uint32_t tag) {
return tagSocket(sockfd, tag, -1);
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 3a3565941489..55626844594d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -110,6 +110,7 @@ public class DynamicSystemInstallationService extends Service
private static final int EVENT_DSU_PROGRESS_UPDATE = 120000;
private static final int EVENT_DSU_INSTALL_COMPLETE = 120001;
private static final int EVENT_DSU_INSTALL_FAILED = 120002;
+ private static final int EVENT_DSU_INSTALL_INSUFFICIENT_SPACE = 120003;
protected static void logEventProgressUpdate(
String partitionName,
@@ -136,6 +137,10 @@ public class DynamicSystemInstallationService extends Service
EventLog.writeEvent(EVENT_DSU_INSTALL_FAILED, cause);
}
+ protected static void logEventInsufficientSpace() {
+ EventLog.writeEvent(EVENT_DSU_INSTALL_INSUFFICIENT_SPACE);
+ }
+
/*
* IPC
*/
@@ -258,6 +263,8 @@ public class DynamicSystemInstallationService extends Service
if (result == RESULT_CANCELLED) {
logEventFailed("Dynamic System installation task is canceled by the user.");
+ } else if (detail instanceof InstallationAsyncTask.InsufficientSpaceException) {
+ logEventInsufficientSpace();
} else {
logEventFailed("error: " + detail);
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags
index 407314384be8..8d8be1030b8e 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags
@@ -5,3 +5,4 @@ option java_package com.android.dynsystem
120000 dsu_progress_update (partition_name|3),(installed_bytes|2|5),(total_bytes|2|5),(partition_number|1|5),(total_partition_number|1|5),(total_progress_percentage|1|5)
120001 dsu_install_complete
120002 dsu_install_failed (cause|3)
+120003 dsu_install_insufficient_space
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 38d851eb76aa..62e53d62fd2a 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -18,6 +18,7 @@ package com.android.dynsystem;
import android.content.Context;
import android.gsi.AvbPublicKey;
+import android.gsi.IGsiService;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -27,6 +28,7 @@ import android.os.SystemProperties;
import android.os.image.DynamicSystemManager;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.webkit.URLUtil;
@@ -106,8 +108,15 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
}
+ static class InsufficientSpaceException extends IOException {
+ InsufficientSpaceException(String message) {
+ super(message);
+ }
+ }
+
/** UNSET means the installation is not completed */
static final int RESULT_UNSET = 0;
+
static final int RESULT_OK = 1;
static final int RESULT_CANCELLED = 2;
static final int RESULT_ERROR_IO = 3;
@@ -157,6 +166,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
private final boolean mIsNetworkUrl;
private final boolean mIsDeviceBootloaderUnlocked;
private final boolean mWantScratchPartition;
+ private int mCreatePartitionStatus;
private DynamicSystemManager.Session mInstallationSession;
private KeyRevocationList mKeyRevocationList;
@@ -364,7 +374,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
mIsZip = true;
} else {
throw new UnsupportedFormatException(
- String.format(Locale.US, "Unsupported file format: %s", mUrl));
+ String.format(Locale.US, "Unsupported file format: %s", mUrl));
}
if (mIsNetworkUrl) {
@@ -435,14 +445,19 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
throws IOException {
Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
- Thread thread = new Thread() {
- @Override
- public void run() {
- mInstallationSession =
- mDynSystem.createPartition(
- partitionName, partitionSize, /* readOnly= */ false);
- }
- };
+ mCreatePartitionStatus = 0;
+ mInstallationSession = null;
+ Thread thread =
+ new Thread() {
+ @Override
+ public void run() {
+ Pair<Integer, DynamicSystemManager.Session> result =
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly = */ false);
+ mCreatePartitionStatus = result.first;
+ mInstallationSession = result.second;
+ }
+ };
initPartitionProgress(partitionName, partitionSize, /* readonly = */ false);
publishProgress(/* installedSize = */ 0L);
@@ -468,13 +483,17 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
}
- if (prevInstalledSize != partitionSize) {
- publishProgress(partitionSize);
- }
-
if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + partitionSize);
+ if (mCreatePartitionStatus == IGsiService.INSTALL_ERROR_NO_SPACE
+ || mCreatePartitionStatus == IGsiService.INSTALL_ERROR_FILE_SYSTEM_CLUTTERED) {
+ throw new InsufficientSpaceException(
+ "Failed to create "
+ + partitionName
+ + " partition: storage media has insufficient free space");
+ } else {
+ throw new IOException(
+ "Failed to start installation with requested size: " + partitionSize);
+ }
}
// Reset installation session and verify that installation completes successfully.
@@ -482,6 +501,11 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
if (!mDynSystem.closePartition()) {
throw new IOException("Failed to complete partition installation: " + partitionName);
}
+
+ // Ensure a 100% mark is published.
+ if (prevInstalledSize != partitionSize) {
+ publishProgress(partitionSize);
+ }
}
private void installScratch() throws IOException {
@@ -606,10 +630,19 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
throw new IOException("Cannot get raw size for " + partitionName);
}
- Thread thread = new Thread(() -> {
- mInstallationSession =
- mDynSystem.createPartition(partitionName, partitionSize, true);
- });
+ mCreatePartitionStatus = 0;
+ mInstallationSession = null;
+ Thread thread =
+ new Thread() {
+ @Override
+ public void run() {
+ Pair<Integer, DynamicSystemManager.Session> result =
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly = */ true);
+ mCreatePartitionStatus = result.first;
+ mInstallationSession = result.second;
+ }
+ };
Log.d(TAG, "Start creating partition: " + partitionName);
thread.start();
@@ -627,8 +660,16 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + partitionSize);
+ if (mCreatePartitionStatus == IGsiService.INSTALL_ERROR_NO_SPACE
+ || mCreatePartitionStatus == IGsiService.INSTALL_ERROR_FILE_SYSTEM_CLUTTERED) {
+ throw new InsufficientSpaceException(
+ "Failed to create "
+ + partitionName
+ + " partition: storage media has insufficient free space");
+ } else {
+ throw new IOException(
+ "Failed to start installation with requested size: " + partitionSize);
+ }
}
Log.d(TAG, "Start installing: " + partitionName);
@@ -688,11 +729,6 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
installedSize += numBytesRead;
}
- // Ensure a 100% mark is published.
- if (prevInstalledSize != partitionSize) {
- publishProgress(partitionSize);
- }
-
AvbPublicKey avbPublicKey = new AvbPublicKey();
if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) {
imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed"));
@@ -708,6 +744,11 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
if (!mDynSystem.closePartition()) {
throw new IOException("Failed to complete partition installation: " + partitionName);
}
+
+ // Ensure a 100% mark is published.
+ if (prevInstalledSize != partitionSize) {
+ publishProgress(partitionSize);
+ }
}
private static String toHexString(byte[] bytes) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index feee4a138a35..c03ed0323bec 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -471,6 +471,9 @@
<!-- Permission needed for CTS test - MatchContentFrameRateTest -->
<uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" />
+ <!-- Permissions needed for manual testing telephony time zone detector behavior -->
+ <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
+
<!-- Permissions needed for CTS test - TimeManagerTest -->
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
<uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" />
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e9dea65c2078..d558336a1285 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -237,7 +237,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
+ sharingIntent.setDataAndType(uri, "image/png");
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
// Include URI in ClipData also, so that grantPermission picks it up.
// We don't use setData here because some apps interpret this as "to:".
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9d190087e300..b7e39a196bd3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,7 +154,6 @@ java_library_static {
"android.hardware.biometrics.fingerprint-V1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.1-java",
- "android.hardware.contexthub-V1.0-java",
"android.hardware.ir-V1-java",
"android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index e924012c8892..99e12a8f9f55 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -119,14 +119,13 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
@Override
@EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public boolean createPartition(String name, long size, boolean readOnly)
- throws RemoteException {
+ public int createPartition(String name, long size, boolean readOnly) throws RemoteException {
IGsiService service = getGsiService();
- if (service.createPartition(name, size, readOnly) != 0) {
- Slog.i(TAG, "Failed to install " + name);
- return false;
+ int status = service.createPartition(name, size, readOnly);
+ if (status != IGsiService.INSTALL_OK) {
+ Slog.i(TAG, "Failed to create partition: " + name);
}
- return true;
+ return status;
}
@Override
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index a83c981235df..d08d90c31134 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -25,41 +25,63 @@ import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
+import android.util.AtomicFile;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
/**
- * A service designed to load and periodically save &quot;randomness&quot;
- * for the Linux kernel RNG.
- *
- * <p>When a Linux system starts up, the entropy pool associated with
- * {@code /dev/random} may be in a fairly predictable state. Applications which
- * depend strongly on randomness may find {@code /dev/random} or
- * {@code /dev/urandom} returning predictable data. In order to counteract
- * this effect, it's helpful to carry the entropy pool information across
- * shutdowns and startups.
+ * A service that loads and periodically saves &quot;randomness&quot; for the
+ * Linux kernel RNG.
*
- * <p>This class was modeled after the script in the
- * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
- * random(4) manual page</a>.
+ * <p>When a Linux system starts up, the entropy pool associated with {@code
+ * /dev/urandom}, {@code /dev/random}, and {@code getrandom()} may be in a
+ * fairly predictable state, depending on the entropy sources available to the
+ * kernel. Applications that depend on randomness may find these APIs returning
+ * predictable data. To counteract this effect, this service maintains a seed
+ * file across shutdowns and startups, and also mixes some device and
+ * boot-specific information into the pool.
*/
public class EntropyMixer extends Binder {
private static final String TAG = "EntropyMixer";
- private static final int ENTROPY_WHAT = 1;
- private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
+ private static final int UPDATE_SEED_MSG = 1;
+ private static final int SEED_UPDATE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
private static final long START_TIME = System.currentTimeMillis();
private static final long START_NANOTIME = System.nanoTime();
- private final String randomDevice;
- private final String entropyFile;
+ /*
+ * The size of the seed file in bytes. This must be at least the size of a
+ * SHA-256 digest (32 bytes). It *should* also be at least the size of the
+ * kernel's entropy pool (/proc/sys/kernel/random/poolsize divided by 8),
+ * which historically was 512 bytes, but changed to 32 bytes in Linux v5.18.
+ * There's actually no real need for more than a 32-byte seed, even with
+ * older kernels; however, we take the conservative approach of staying with
+ * the 512-byte size for now, as the cost is very small.
+ */
+ @VisibleForTesting
+ static final int SEED_FILE_SIZE = 512;
+
+ @VisibleForTesting
+ static final String DEVICE_SPECIFIC_INFO_HEADER =
+ "Copyright (C) 2009 The Android Open Source Project\n" +
+ "All Your Randomness Are Belong To Us\n";
+
+ private final AtomicFile seedFile;
+ private final File randomReadDevice;
+ private final File randomWriteDevice; // separate from randomReadDevice only for testing
/**
- * Handler that periodically updates the entropy on disk.
+ * Handler that periodically updates the seed file.
*/
private final Handler mHandler = new Handler(IoThread.getHandler().getLooper()) {
// IMPLEMENTATION NOTE: This handler runs on the I/O thread to avoid I/O on the main thread.
@@ -67,40 +89,36 @@ public class EntropyMixer extends Binder {
// own ID space for the "what" parameter of messages seen by the handler.
@Override
public void handleMessage(Message msg) {
- if (msg.what != ENTROPY_WHAT) {
+ if (msg.what != UPDATE_SEED_MSG) {
Slog.e(TAG, "Will not process invalid message");
return;
}
- writeEntropy();
- scheduleEntropyWriter();
+ updateSeedFile();
+ scheduleSeedUpdater();
}
};
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- writeEntropy();
+ updateSeedFile();
}
};
public EntropyMixer(Context context) {
- this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
+ this(context, new File(getSystemDir(), "entropy.dat"),
+ new File("/dev/urandom"), new File("/dev/urandom"));
}
- /** Test only interface, not for public use */
- public EntropyMixer(
- Context context,
- String entropyFile,
- String randomDevice) {
- if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
- if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
+ @VisibleForTesting
+ EntropyMixer(Context context, File seedFile, File randomReadDevice, File randomWriteDevice) {
+ this.seedFile = new AtomicFile(Preconditions.checkNotNull(seedFile));
+ this.randomReadDevice = Preconditions.checkNotNull(randomReadDevice);
+ this.randomWriteDevice = Preconditions.checkNotNull(randomWriteDevice);
- this.randomDevice = randomDevice;
- this.entropyFile = entropyFile;
loadInitialEntropy();
- addDeviceSpecificEntropy();
- writeEntropy();
- scheduleEntropyWriter();
+ updateSeedFile();
+ scheduleSeedUpdater();
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED);
broadcastFilter.addAction(Intent.ACTION_REBOOT);
@@ -112,76 +130,147 @@ public class EntropyMixer extends Binder {
);
}
- private void scheduleEntropyWriter() {
- mHandler.removeMessages(ENTROPY_WHAT);
- mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD);
+ private void scheduleSeedUpdater() {
+ mHandler.removeMessages(UPDATE_SEED_MSG);
+ mHandler.sendEmptyMessageDelayed(UPDATE_SEED_MSG, SEED_UPDATE_PERIOD);
}
private void loadInitialEntropy() {
- try {
- RandomBlock.fromFile(entropyFile).toFile(randomDevice, false);
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "No existing entropy file -- first boot?");
+ byte[] seed = readSeedFile();
+ try (FileOutputStream out = new FileOutputStream(randomWriteDevice)) {
+ if (seed.length != 0) {
+ out.write(seed);
+ Slog.i(TAG, "Loaded existing seed file");
+ }
+ out.write(getDeviceSpecificInformation());
} catch (IOException e) {
- Slog.w(TAG, "Failure loading existing entropy file", e);
+ Slog.e(TAG, "Error writing to " + randomWriteDevice, e);
}
}
- private void writeEntropy() {
+ private byte[] readSeedFile() {
try {
- Slog.i(TAG, "Writing entropy...");
- RandomBlock.fromFile(randomDevice).toFile(entropyFile, true);
+ return seedFile.readFully();
+ } catch (FileNotFoundException e) {
+ return new byte[0];
} catch (IOException e) {
- Slog.w(TAG, "Unable to write entropy", e);
+ Slog.e(TAG, "Error reading " + seedFile.getBaseFile(), e);
+ return new byte[0];
}
}
/**
- * Add additional information to the kernel entropy pool. The
- * information isn't necessarily "random", but that's ok. Even
- * sending non-random information to {@code /dev/urandom} is useful
- * because, while it doesn't increase the "quality" of the entropy pool,
- * it mixes more bits into the pool, which gives us a higher degree
- * of uncertainty in the generated randomness. Like nature, writes to
- * the random device can only cause the quality of the entropy in the
- * kernel to stay the same or increase.
+ * Update (or create) the seed file.
+ *
+ * <p>Traditionally, the recommended way to update a seed file on Linux was
+ * to simply copy some bytes from /dev/urandom. However, that isn't
+ * actually a good way to do it, because writes to /dev/urandom aren't
+ * guaranteed to immediately affect reads from /dev/urandom. This can cause
+ * the new seed file to contain less entropy than the old one!
*
- * <p>For maximum effect, we try to target information which varies
- * on a per-device basis, and is not easily observable to an
- * attacker.
+ * <p>Instead, we generate the new seed by hashing the old seed together
+ * with some bytes from /dev/urandom, following the example of <a
+ * href="https://git.zx2c4.com/seedrng/tree/README.md">SeedRNG</a>. This
+ * ensures that the new seed is at least as entropic as the old seed.
*/
- private void addDeviceSpecificEntropy() {
- PrintWriter out = null;
+ private void updateSeedFile() {
+ byte[] oldSeed = readSeedFile();
+ byte[] newSeed = new byte[SEED_FILE_SIZE];
+
+ try (FileInputStream in = new FileInputStream(randomReadDevice)) {
+ if (in.read(newSeed) != newSeed.length) {
+ throw new IOException("unexpected EOF");
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + randomReadDevice +
+ "; seed file won't be properly updated", e);
+ // Continue on; at least we'll have new timestamps...
+ }
+
+ // newSeed = newSeed[:-32] ||
+ // SHA-256(fixed_prefix || real_time || boot_time ||
+ // old_seed_len || old_seed || new_seed_len || new_seed)
+ MessageDigest sha256;
try {
- out = new PrintWriter(new FileOutputStream(randomDevice));
- out.println("Copyright (C) 2009 The Android Open Source Project");
- out.println("All Your Randomness Are Belong To Us");
- out.println(START_TIME);
- out.println(START_NANOTIME);
- out.println(SystemProperties.get("ro.serialno"));
- out.println(SystemProperties.get("ro.bootmode"));
- out.println(SystemProperties.get("ro.baseband"));
- out.println(SystemProperties.get("ro.carrier"));
- out.println(SystemProperties.get("ro.bootloader"));
- out.println(SystemProperties.get("ro.hardware"));
- out.println(SystemProperties.get("ro.revision"));
- out.println(SystemProperties.get("ro.build.fingerprint"));
- out.println(new Object().hashCode());
- out.println(System.currentTimeMillis());
- out.println(System.nanoTime());
+ sha256 = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ Slog.wtf(TAG, "SHA-256 algorithm not found; seed file won't be updated", e);
+ return;
+ }
+ // This fixed prefix should be changed if the fields that are hashed change.
+ sha256.update("Android EntropyMixer v1".getBytes());
+ sha256.update(longToBytes(System.currentTimeMillis()));
+ sha256.update(longToBytes(System.nanoTime()));
+ sha256.update(longToBytes(oldSeed.length));
+ sha256.update(oldSeed);
+ sha256.update(longToBytes(newSeed.length));
+ sha256.update(newSeed);
+ byte[] digest = sha256.digest();
+ System.arraycopy(digest, 0, newSeed, newSeed.length - digest.length, digest.length);
+
+ writeNewSeed(newSeed);
+ if (oldSeed.length == 0) {
+ Slog.i(TAG, "Created seed file");
+ } else {
+ Slog.i(TAG, "Updated seed file");
+ }
+ }
+
+ private void writeNewSeed(byte[] newSeed) {
+ FileOutputStream out = null;
+ try {
+ out = seedFile.startWrite();
+ out.write(newSeed);
+ seedFile.finishWrite(out);
} catch (IOException e) {
- Slog.w(TAG, "Unable to add device specific data to the entropy pool", e);
- } finally {
- if (out != null) {
- out.close();
- }
+ Slog.e(TAG, "Error writing " + seedFile.getBaseFile(), e);
+ seedFile.failWrite(out);
}
}
- private static String getSystemDir() {
+ private static byte[] longToBytes(long x) {
+ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+ buffer.putLong(x);
+ return buffer.array();
+ }
+
+ /**
+ * Get some device and boot-specific information to mix into the kernel's
+ * entropy pool. This information probably won't contain much actual
+ * entropy, but that's fine because we don't ask the kernel to credit it.
+ * Writes to {@code /dev/urandom} can only increase or have no effect on the
+ * quality of random numbers, never decrease it.
+ *
+ * <p>The main goal here is just to initialize the entropy pool differently
+ * on devices that might otherwise be identical and have very little other
+ * entropy available. Therefore, we include various system properties that
+ * can vary on a per-device and/or per-build basis. We also include some
+ * timestamps, as these might vary on a per-boot basis and be not easily
+ * observable or guessable by an attacker.
+ */
+ private byte[] getDeviceSpecificInformation() {
+ StringBuilder b = new StringBuilder();
+ b.append(DEVICE_SPECIFIC_INFO_HEADER);
+ b.append(START_TIME).append('\n');
+ b.append(START_NANOTIME).append('\n');
+ b.append(SystemProperties.get("ro.serialno")).append('\n');
+ b.append(SystemProperties.get("ro.bootmode")).append('\n');
+ b.append(SystemProperties.get("ro.baseband")).append('\n');
+ b.append(SystemProperties.get("ro.carrier")).append('\n');
+ b.append(SystemProperties.get("ro.bootloader")).append('\n');
+ b.append(SystemProperties.get("ro.hardware")).append('\n');
+ b.append(SystemProperties.get("ro.revision")).append('\n');
+ b.append(SystemProperties.get("ro.build.fingerprint")).append('\n');
+ b.append(new Object().hashCode()).append('\n');
+ b.append(System.currentTimeMillis()).append('\n');
+ b.append(System.nanoTime()).append('\n');
+ return b.toString().getBytes();
+ }
+
+ private static File getSystemDir() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
- return systemDir.toString();
+ return systemDir;
}
}
diff --git a/services/core/java/com/android/server/RandomBlock.java b/services/core/java/com/android/server/RandomBlock.java
deleted file mode 100644
index 6d6d9010a8d9..000000000000
--- a/services/core/java/com/android/server/RandomBlock.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.util.Slog;
-
-import java.io.Closeable;
-import java.io.DataOutput;
-import java.io.EOFException;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-
-/**
- * A block of 512 random {@code byte}s.
- */
-class RandomBlock {
-
- private static final String TAG = "RandomBlock";
- private static final boolean DEBUG = false;
- private static final int BLOCK_SIZE = 512;
- private byte[] block = new byte[BLOCK_SIZE];
-
- private RandomBlock() { }
-
- static RandomBlock fromFile(String filename) throws IOException {
- if (DEBUG) Slog.v(TAG, "reading from file " + filename);
- InputStream stream = null;
- try {
- stream = new FileInputStream(filename);
- return fromStream(stream);
- } finally {
- close(stream);
- }
- }
-
- private static RandomBlock fromStream(InputStream in) throws IOException {
- RandomBlock retval = new RandomBlock();
- int total = 0;
- while(total < BLOCK_SIZE) {
- int result = in.read(retval.block, total, BLOCK_SIZE - total);
- if (result == -1) {
- throw new EOFException();
- }
- total += result;
- }
- return retval;
- }
-
- void toFile(String filename, boolean sync) throws IOException {
- if (DEBUG) Slog.v(TAG, "writing to file " + filename);
- RandomAccessFile out = null;
- try {
- out = new RandomAccessFile(filename, sync ? "rws" : "rw");
- toDataOut(out);
- truncateIfPossible(out);
- } finally {
- close(out);
- }
- }
-
- private static void truncateIfPossible(RandomAccessFile f) {
- try {
- f.setLength(BLOCK_SIZE);
- } catch (IOException e) {
- // ignore this exception. Sometimes, the file we're trying to
- // write is a character device, such as /dev/urandom, and
- // these character devices do not support setting the length.
- }
- }
-
- private void toDataOut(DataOutput out) throws IOException {
- out.write(block);
- }
-
- private static void close(Closeable c) {
- try {
- if (c == null) {
- return;
- }
- c.close();
- } catch (IOException e) {
- Slog.w(TAG, "IOException thrown while closing Closeable", e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a6da4a6a4260..35217dba7c28 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2692,6 +2692,9 @@ public class Vpn {
return; // VPN has been shut down.
}
+ // Clear mInterface to prevent Ikev2VpnRunner being cleared when
+ // interfaceRemoved() is called.
+ mInterface = null;
// Without MOBIKE, we have no way to seamlessly migrate. Close on old
// (non-default) network, and start the new one.
resetIkeState();
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 58d6dae1637a..68dcc7d7fb50 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -16,26 +16,116 @@
package com.android.server;
+import static org.junit.Assert.assertArrayEquals;
+
import android.content.Context;
-import android.os.FileUtils;
import android.test.AndroidTestCase;
+import org.junit.Test;
+
import java.io.File;
+import java.nio.file.Files;
+import java.util.Arrays;
/**
* Tests for {@link com.android.server.EntropyMixer}
*/
public class EntropyMixerTest extends AndroidTestCase {
- public void testInitialWrite() throws Exception {
- File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE);
- File file = File.createTempFile("testInitialWrite", "dat", dir);
+ private static final int SEED_FILE_SIZE = EntropyMixer.SEED_FILE_SIZE;
+
+ private File dir;
+ private File seedFile;
+ private File randomReadDevice;
+ private File randomWriteDevice;
+
+ @Override
+ public void setUp() throws Exception {
+ dir = getContext().getDir("test", Context.MODE_PRIVATE);
+ seedFile = createTempFile(dir, "entropy.dat");
+ randomReadDevice = createTempFile(dir, "urandomRead");
+ randomWriteDevice = createTempFile(dir, "urandomWrite");
+ }
+
+ private File createTempFile(File dir, String prefix) throws Exception {
+ File file = File.createTempFile(prefix, null, dir);
file.deleteOnExit();
- assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
+ return file;
+ }
+
+ private byte[] repeatByte(byte b, int length) {
+ byte[] data = new byte[length];
+ Arrays.fill(data, b);
+ return data;
+ }
+
+ // Test initializing the EntropyMixer when the seed file doesn't exist yet.
+ @Test
+ public void testInitFirstBoot() throws Exception {
+ seedFile.delete();
+
+ byte[] urandomInjectedData = repeatByte((byte) 0x01, SEED_FILE_SIZE);
+ Files.write(randomReadDevice.toPath(), urandomInjectedData);
- // The constructor has the side effect of writing to file
- new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
+ // The constructor should have the side effect of writing to
+ // randomWriteDevice and creating seedFile.
+ new EntropyMixer(getContext(), seedFile, randomReadDevice, randomWriteDevice);
+
+ // Since there was no old seed file, the data that was written to
+ // randomWriteDevice should contain only device-specific information.
+ assertTrue(isDeviceSpecificInfo(Files.readAllBytes(randomWriteDevice.toPath())));
+
+ // The seed file should have been created.
+ validateSeedFile(seedFile, new byte[0], urandomInjectedData);
+ }
+
+ // Test initializing the EntropyMixer when the seed file already exists.
+ @Test
+ public void testInitNonFirstBoot() throws Exception {
+ byte[] previousSeed = repeatByte((byte) 0x01, SEED_FILE_SIZE);
+ Files.write(seedFile.toPath(), previousSeed);
+
+ byte[] urandomInjectedData = repeatByte((byte) 0x02, SEED_FILE_SIZE);
+ Files.write(randomReadDevice.toPath(), urandomInjectedData);
+
+ // The constructor should have the side effect of writing to
+ // randomWriteDevice and updating seedFile.
+ new EntropyMixer(getContext(), seedFile, randomReadDevice, randomWriteDevice);
+
+ // The data that was written to randomWriteDevice should consist of the
+ // previous seed followed by the device-specific information.
+ byte[] dataWrittenToUrandom = Files.readAllBytes(randomWriteDevice.toPath());
+ byte[] firstPartWritten = Arrays.copyOf(dataWrittenToUrandom, SEED_FILE_SIZE);
+ byte[] secondPartWritten =
+ Arrays.copyOfRange(
+ dataWrittenToUrandom, SEED_FILE_SIZE, dataWrittenToUrandom.length);
+ assertArrayEquals(previousSeed, firstPartWritten);
+ assertTrue(isDeviceSpecificInfo(secondPartWritten));
+
+ // The seed file should have been updated.
+ validateSeedFile(seedFile, previousSeed, urandomInjectedData);
+ }
+
+ private boolean isDeviceSpecificInfo(byte[] data) {
+ return new String(data).startsWith(EntropyMixer.DEVICE_SPECIFIC_INFO_HEADER);
+ }
- assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
+ private void validateSeedFile(File seedFile, byte[] previousSeed, byte[] urandomInjectedData)
+ throws Exception {
+ final int unhashedLen = SEED_FILE_SIZE - 32;
+ byte[] newSeed = Files.readAllBytes(seedFile.toPath());
+ assertEquals(SEED_FILE_SIZE, newSeed.length);
+ assertEquals(SEED_FILE_SIZE, urandomInjectedData.length);
+ assertFalse(Arrays.equals(newSeed, previousSeed));
+ // The new seed should consist of the first SEED_FILE_SIZE - 32 bytes
+ // that were read from urandom, followed by a 32-byte hash that should
+ // *not* be the same as the last 32 bytes that were read from urandom.
+ byte[] firstPart = Arrays.copyOf(newSeed, unhashedLen);
+ byte[] secondPart = Arrays.copyOfRange(newSeed, unhashedLen, SEED_FILE_SIZE);
+ byte[] firstPartInjected = Arrays.copyOf(urandomInjectedData, unhashedLen);
+ byte[] secondPartInjected =
+ Arrays.copyOfRange(urandomInjectedData, unhashedLen, SEED_FILE_SIZE);
+ assertArrayEquals(firstPart, firstPartInjected);
+ assertFalse(Arrays.equals(secondPart, secondPartInjected));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/OWNERS b/services/tests/servicestests/src/com/android/server/appwidget/OWNERS
new file mode 100644
index 000000000000..d724cac4aa3e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appwidget/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/appwidget/OWNERS
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 281b018b3395..b0ddf2c28dff 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -369,6 +369,10 @@ public class ApnSetting implements Parcelable {
public @interface AuthType {}
// Possible values for protocol which is defined in TS 27.007 section 10.1.1.
+ /** Unknown protocol.
+ * @hide
+ */
+ public static final int PROTOCOL_UNKNOWN = -1;
/** Internet protocol. */
public static final int PROTOCOL_IP = 0;
/** Internet protocol, version 6. */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 883e2ad9b76e..7799113c5bf1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2556,4 +2556,7 @@ interface ITelephony {
* for the slot, or {@code null} if none is resolved
*/
String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex);
+
+ /** Check if telephony new data stack is enabled. */
+ boolean isUsingNewDataStack();
}