Merge "Use PackageManagerInternal instead of PackageManager in PlatformCompat."
diff --git a/api/current.txt b/api/current.txt
index d69032d..c86506b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35911,6 +35911,7 @@
method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor);
method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
+ method public boolean isCheckpointSupported();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(String);
method public boolean mountObb(String, String, android.os.storage.OnObbStateChangeListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index f3cdac3..77aff8d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4853,6 +4853,10 @@
field @Deprecated public static final int WPA2_PSK = 4; // 0x4
}
+ public class WifiFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
public class WifiInfo implements android.os.Parcelable {
method @Nullable public String getAppPackageName();
method public double getRxSuccessRate();
@@ -6230,6 +6234,7 @@
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
@@ -6540,6 +6545,7 @@
field public static final String SERIAL_NUMBER = "serial_number";
field public static final String SERVICE_CATEGORY = "service_category";
field public static final String SLOT_INDEX = "slot_index";
+ field public static final String SUB_ID = "sub_id";
}
public static final class Telephony.Sms.Intents {
@@ -8599,7 +8605,7 @@
}
public final class SmsCbMessage implements android.os.Parcelable {
- ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int, @Nullable java.util.List<android.telephony.CbGeoUtils.Geometry>, long, int);
+ ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int, @Nullable java.util.List<android.telephony.CbGeoUtils.Geometry>, long, int, int);
method @NonNull public static android.telephony.SmsCbMessage createFromCursor(@NonNull android.database.Cursor);
method public int describeContents();
method @Nullable public android.telephony.SmsCbCmasInfo getCmasWarningInfo();
@@ -8616,6 +8622,7 @@
method public int getSerialNumber();
method public int getServiceCategory();
method public int getSlotIndex();
+ method public int getSubscriptionId();
method public boolean isCmasMessage();
method public boolean isEmergencyMessage();
method public boolean isEtwsMessage();
diff --git a/api/test-current.txt b/api/test-current.txt
index a8bd9be..693b190 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2526,6 +2526,7 @@
field public static final String SERIAL_NUMBER = "serial_number";
field public static final String SERVICE_CATEGORY = "service_category";
field public static final String SLOT_INDEX = "slot_index";
+ field public static final String SUB_ID = "sub_id";
}
public static final class Telephony.Sms.Intents {
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index 68fb8e6..e602e2a 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -94,7 +94,7 @@
COMMAND_HELP,
new PowerCommand(),
new DataCommand(),
- new WifiCommand(),
+ // `svc wifi` has been migrated to WifiShellCommand
new UsbCommand(),
new NfcCommand(),
new BluetoothCommand(),
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
deleted file mode 100644
index e31cb53..0000000
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2008 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.commands.svc;
-
-import android.os.ServiceManager;
-import android.os.RemoteException;
-import android.net.wifi.IWifiManager;
-import android.content.Context;
-
-public class WifiCommand extends Svc.Command {
- public WifiCommand() {
- super("wifi");
- }
-
- public String shortHelp() {
- return "Control the Wi-Fi manager";
- }
-
- public String longHelp() {
- return shortHelp() + "\n"
- + "\n"
- + "usage: svc wifi [enable|disable]\n"
- + " Turn Wi-Fi on or off.\n\n";
- }
-
- public void run(String[] args) {
- boolean validCommand = false;
- if (args.length >= 2) {
- boolean flag = false;
- if ("enable".equals(args[1])) {
- flag = true;
- validCommand = true;
- } else if ("disable".equals(args[1])) {
- flag = false;
- validCommand = true;
- }
- if (validCommand) {
- IWifiManager wifiMgr
- = IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
- if (wifiMgr == null) {
- System.err.println("Wi-Fi service is not ready");
- return;
- }
- try {
- wifiMgr.setWifiEnabled("com.android.shell", flag);
- }
- catch (RemoteException e) {
- System.err.println("Wi-Fi operation failed: " + e);
- }
- return;
- }
- }
- System.err.println(longHelp());
- }
-}
diff --git a/cmds/svc/svc b/cmds/svc/svc
index c122e98..60c95c7 100755
--- a/cmds/svc/svc
+++ b/cmds/svc/svc
@@ -1,3 +1,24 @@
#!/system/bin/sh
+
+# `svc wifi` has been migrated to WifiShellCommand,
+# simply perform translation to `cmd wifi set-wifi-enabled` here.
+if [ "x$1" == "xwifi" ]; then
+ # `cmd wifi` by convention uses enabled/disabled
+ # instead of enable/disable
+ if [ "x$2" == "xenable" ]; then
+ exec cmd wifi set-wifi-enabled enabled
+ elif [ "x$2" == "xdisable" ]; then
+ exec cmd wifi set-wifi-enabled disabled
+ else
+ echo "Control the Wi-Fi manager"
+ echo ""
+ echo "usage: svc wifi [enable|disable]"
+ echo " Turn Wi-Fi on or off."
+ echo ""
+ fi
+ exit 1
+fi
+
export CLASSPATH=/system/framework/svc.jar
exec app_process /system/bin com.android.commands.svc.Svc "$@"
+
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index afb7871..5032f77 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1213,7 +1213,6 @@
OP_START_FOREGROUND,
OP_SMS_FINANCIAL_TRANSACTIONS,
OP_MANAGE_IPSEC_TUNNELS,
- OP_GET_USAGE_STATS,
OP_INSTANT_APP_START_FOREGROUND
};
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5effa1b..0bfd2c6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -116,16 +116,7 @@
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
-import android.net.wifi.IWifiScanner;
-import android.net.wifi.RttManager;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiScanner;
-import android.net.wifi.aware.IWifiAwareManager;
-import android.net.wifi.aware.WifiAwareManager;
-import android.net.wifi.p2p.IWifiP2pManager;
-import android.net.wifi.p2p.WifiP2pManager;
-import android.net.wifi.rtt.IWifiRttManager;
-import android.net.wifi.rtt.WifiRttManager;
+import android.net.wifi.WifiFrameworkInitializer;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -691,66 +682,6 @@
ConnectivityThread.getInstanceLooper());
}});
- registerService(Context.WIFI_SERVICE, WifiManager.class,
- new CachedServiceFetcher<WifiManager>() {
- @Override
- public WifiManager createService(ContextImpl ctx) {
- return new WifiManager(ctx.getOuterContext(),
- ConnectivityThread.getInstanceLooper());
- }});
-
- registerService(Context.WIFI_P2P_SERVICE, WifiP2pManager.class,
- new StaticServiceFetcher<WifiP2pManager>() {
- @Override
- public WifiP2pManager createService() throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_P2P_SERVICE);
- IWifiP2pManager service = IWifiP2pManager.Stub.asInterface(b);
- return new WifiP2pManager(service);
- }});
-
- registerService(Context.WIFI_AWARE_SERVICE, WifiAwareManager.class,
- new CachedServiceFetcher<WifiAwareManager>() {
- @Override
- public WifiAwareManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_AWARE_SERVICE);
- IWifiAwareManager service = IWifiAwareManager.Stub.asInterface(b);
- if (service == null) {
- return null;
- }
- return new WifiAwareManager(ctx.getOuterContext(), service);
- }});
-
- registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class,
- new CachedServiceFetcher<WifiScanner>() {
- @Override
- public WifiScanner createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SCANNING_SERVICE);
- IWifiScanner service = IWifiScanner.Stub.asInterface(b);
- return new WifiScanner(ctx.getOuterContext(), service,
- ConnectivityThread.getInstanceLooper());
- }});
-
- registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
- new CachedServiceFetcher<RttManager>() {
- @Override
- public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
- IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
- return new RttManager(ctx.getOuterContext(),
- new WifiRttManager(ctx.getOuterContext(), service));
- }});
-
- registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
- new CachedServiceFetcher<WifiRttManager>() {
- @Override
- public WifiRttManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(
- Context.WIFI_RTT_RANGING_SERVICE);
- IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
- return new WifiRttManager(ctx.getOuterContext(), service);
- }});
-
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
new CachedServiceFetcher<EthernetManager>() {
@Override
@@ -1265,6 +1196,7 @@
BlobStoreManagerFrameworkInitializer.initialize();
TelephonyFrameworkInitializer.registerServiceWrappers();
AppSearchManagerFrameworkInitializer.initialize();
+ WifiFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index deeeddc..2d70986d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2362,6 +2362,19 @@
}
}
+ /**
+ * Check whether the device supports filesystem checkpoint.
+ *
+ * @return true if the device supports filesystem checkpoint, false otherwise.
+ */
+ public boolean isCheckpointSupported() {
+ try {
+ return mStorageManager.supportsCheckpoint();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final Object mFuseAppLoopLock = new Object();
@GuardedBy("mFuseAppLoopLock")
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 1d7e338..abf34ca 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -391,8 +391,9 @@
* Look up the values of multiple properties for a particular namespace. The lookup is atomic,
* such that the values of these properties cannot change between the time when the first is
* fetched and the time when the last is fetched.
- *
- * TODO: reference setProperties when it is added.
+ * <p>
+ * Each call to {@link #setProperties(Properties)} is also atomic and ensures that either none
+ * or all of the change is picked up here, but never only part of it.
*
* @param namespace The namespace containing the properties to look up.
* @param names The names of properties to look up, or empty to fetch all properties for the
@@ -560,6 +561,27 @@
}
/**
+ * Set all of the properties for a specific namespace. Pre-existing properties will be updated
+ * and new properties will be added if necessary. Any pre-existing properties for the specific
+ * namespace which are not part of the provided {@link Properties} object will be deleted from
+ * the namespace. These changes are all applied atomically, such that no calls to read or reset
+ * these properties can happen in the middle of this update.
+ * <p>
+ * Each call to {@link #getProperties(String, String...)} is also atomic and ensures that either
+ * none or all of this update is picked up, but never only part of it.
+ *
+ * @param properties the complete set of properties to set for a specific namespace.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean setProperties(@NonNull Properties properties) {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
+ properties.mMap);
+ }
+
+ /**
* Reset properties to their default values.
* <p>
* The method accepts an optional namespace parameter. If provided, only properties set within
@@ -703,23 +725,26 @@
List<String> pathSegments = uri.getPathSegments();
// pathSegments(0) is "config"
final String namespace = pathSegments.get(1);
- final String name = pathSegments.get(2);
- final String value;
+ Map<String, String> propertyMap = new ArrayMap<>();
try {
- value = getProperty(namespace, name);
+ Properties allProperties = getProperties(namespace);
+ for (int i = 2; i < pathSegments.size(); ++i) {
+ String key = pathSegments.get(i);
+ propertyMap.put(key, allProperties.getString(key, null));
+ }
} catch (SecurityException e) {
// Silently failing to not crash binder or listener threads.
Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
return;
}
+ Properties properties = new Properties(namespace, propertyMap);
+
synchronized (sLock) {
for (int i = 0; i < sListeners.size(); i++) {
if (namespace.equals(sListeners.valueAt(i).first)) {
final OnPropertiesChangedListener listener = sListeners.keyAt(i);
sListeners.valueAt(i).second.execute(() -> {
- Map<String, String> propertyMap = new ArrayMap<>(1);
- propertyMap.put(name, value);
- listener.onPropertiesChanged(new Properties(namespace, propertyMap));
+ listener.onPropertiesChanged(properties);
});
}
}
@@ -741,7 +766,11 @@
}
/**
- * Interface for monitoring changes to properties.
+ * Interface for monitoring changes to properties. Implementations will receive callbacks when
+ * properties change, including a {@link Properties} object which contains a single namespace
+ * and all of the properties which changed for that namespace. This includes properties which
+ * were added, updated, or deleted. This is not necessarily a complete list of all properties
+ * belonging to the namespace, as properties which don't change are omitted.
* <p>
* Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
*
@@ -751,10 +780,13 @@
@TestApi
public interface OnPropertiesChangedListener {
/**
- * Called when one or more properties have changed.
+ * Called when one or more properties have changed, providing a Properties object with all
+ * of the changed properties. This object will contain only properties which have changed,
+ * not the complete set of all properties belonging to the namespace.
*
* @param properties Contains the complete collection of properties which have changed for a
- * single namespace.
+ * single namespace. This includes only those which were added, updated,
+ * or deleted.
*/
void onPropertiesChanged(@NonNull Properties properties);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3ac7deb..cce0d4f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1960,6 +1960,11 @@
*/
public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
+ /**
+ * @hide - String argument extra to the fast-path call()-based requests
+ */
+ public static final String CALL_METHOD_FLAGS_KEY = "_flags";
+
/** @hide - Private call() method to write to 'system' table */
public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
@@ -1972,6 +1977,9 @@
/** @hide - Private call() method to write to 'configuration' table */
public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
+ /** @hide - Private call() method to write to and delete from the 'configuration' table */
+ public static final String CALL_METHOD_SET_ALL_CONFIG = "SET_ALL_config";
+
/** @hide - Private call() method to delete from the 'system' table */
public static final String CALL_METHOD_DELETE_SYSTEM = "DELETE_system";
@@ -2304,21 +2312,23 @@
private final String mCallGetCommand;
private final String mCallSetCommand;
private final String mCallListCommand;
+ private final String mCallSetAllCommand;
@GuardedBy("this")
private GenerationTracker mGenerationTracker;
public NameValueCache(Uri uri, String getCommand, String setCommand,
ContentProviderHolder providerHolder) {
- this(uri, getCommand, setCommand, null, providerHolder);
+ this(uri, getCommand, setCommand, null, null, providerHolder);
}
NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand,
- ContentProviderHolder providerHolder) {
+ String setAllCommand, ContentProviderHolder providerHolder) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
mCallListCommand = listCommand;
+ mCallSetAllCommand = setAllCommand;
mProviderHolder = providerHolder;
}
@@ -2344,6 +2354,26 @@
return true;
}
+ public boolean setStringsForPrefix(ContentResolver cr, String prefix,
+ HashMap<String, String> keyValues) {
+ if (mCallSetAllCommand == null) {
+ // This NameValueCache does not support atomically setting multiple flags
+ return false;
+ }
+ try {
+ Bundle args = new Bundle();
+ args.putString(CALL_METHOD_PREFIX_KEY, prefix);
+ args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
+ IContentProvider cp = mProviderHolder.getProvider(cr);
+ cp.call(cr.getPackageName(), cr.getFeatureId(), mProviderHolder.mUri.getAuthority(),
+ mCallSetAllCommand, null, args);
+ } catch (RemoteException e) {
+ // Not supported by the remote side
+ return false;
+ }
+ return true;
+ }
+
@UnsupportedAppUsage
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
final boolean isSelf = (userHandle == UserHandle.myUserId());
@@ -13718,6 +13748,7 @@
CALL_METHOD_GET_CONFIG,
CALL_METHOD_PUT_CONFIG,
CALL_METHOD_LIST_CONFIG,
+ CALL_METHOD_SET_ALL_CONFIG,
sProviderHolder);
/**
@@ -13792,6 +13823,29 @@
}
/**
+ * Clear all name/value pairs for the provided namespace and save new name/value pairs in
+ * their place.
+ *
+ * @param resolver to access the database with.
+ * @param namespace to which the names should be set.
+ * @param keyValues map of key names (without the prefix) to values.
+ * @return
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static boolean setStrings(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull Map<String, String> keyValues) {
+ HashMap<String, String> compositeKeyValueMap = new HashMap<>(keyValues.keySet().size());
+ for (Map.Entry<String, String> entry : keyValues.entrySet()) {
+ compositeKeyValueMap.put(
+ createCompositeName(namespace, entry.getKey()), entry.getValue());
+ }
+ return sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
+ compositeKeyValueMap);
+ }
+
+ /**
* 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/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 0c83390..8c1c3b5 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -33,6 +33,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+import java.util.Map;
+
/** Tests that ensure appropriate settings are backed up. */
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -484,12 +487,74 @@
assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
}
- // TODO(mpape): resolve b/142727848 and re-enable this test
+ @Test
+ public void setProperties() {
+ Map<String, String> keyValues = new HashMap<>();
+ keyValues.put(KEY, VALUE);
+ keyValues.put(KEY2, VALUE2);
+
+ DeviceConfig.setProperties(new Properties(NAMESPACE, keyValues));
+ Properties properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+ Map<String, String> newKeyValues = new HashMap<>();
+ newKeyValues.put(KEY, VALUE2);
+ newKeyValues.put(KEY3, VALUE3);
+
+ DeviceConfig.setProperties(new Properties(NAMESPACE, newKeyValues));
+ properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY3);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE3);
+
+ assertThat(properties.getKeyset()).doesNotContain(KEY2);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+ }
+
+ @Test
+ public void setProperties_multipleNamespaces() {
+ Map<String, String> keyValues = new HashMap<>();
+ keyValues.put(KEY, VALUE);
+ keyValues.put(KEY2, VALUE2);
+
+ Map<String, String> keyValues2 = new HashMap<>();
+ keyValues2.put(KEY2, VALUE);
+ keyValues2.put(KEY3, VALUE2);
+
+ final String namespace2 = "namespace2";
+ DeviceConfig.setProperties(new Properties(NAMESPACE, keyValues));
+ DeviceConfig.setProperties(new Properties(namespace2, keyValues2));
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+ assertThat(properties.getKeyset()).doesNotContain(KEY3);
+ assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+
+ properties = DeviceConfig.getProperties(namespace2);
+ assertThat(properties.getKeyset()).containsExactly(KEY2, KEY3);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+ assertThat(properties.getKeyset()).doesNotContain(KEY);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+
+ // clean up
+ deleteViaContentProvider(namespace2, KEY);
+ deleteViaContentProvider(namespace2, KEY2);
+ deleteViaContentProvider(namespace2, KEY3);
+ }
+
+ // TODO(mpape): resolve b/142727848 and re-enable listener tests
// @Test
-// public void testOnPropertiesChangedListener() throws InterruptedException {
+// public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
// final CountDownLatch countDownLatch = new CountDownLatch(1);
//
-// OnPropertiesChangedListener changeListener = (properties) -> {
+// DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
// assertThat(properties.getKeyset()).contains(KEY);
// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
@@ -508,6 +573,42 @@
// DeviceConfig.removeOnPropertiesChangedListener(changeListener);
// }
// }
+//
+// @Test
+// public void onPropertiesChangedListener_setPropertiesCallback() throws InterruptedException {
+// final CountDownLatch countDownLatch = new CountDownLatch(1);
+// DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+// DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+//
+// Map<String, String> keyValues = new HashMap<>(2);
+// keyValues.put(KEY, VALUE2);
+// keyValues.put(KEY3, VALUE3);
+// Properties setProperties = new Properties(NAMESPACE, keyValues);
+//
+// DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
+// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+// assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+// // KEY updated from VALUE to VALUE2
+// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
+// // KEY2 deleted (returns default_value)
+// assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
+// //KEY3 added with VALUE3
+// assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
+// countDownLatch.countDown();
+// };
+//
+// try {
+// DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
+// ActivityThread.currentApplication().getMainExecutor(), changeListener);
+// DeviceConfig.setProperties(setProperties);
+// assertThat(countDownLatch.await(
+// WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+// } catch (InterruptedException e) {
+// Assert.fail(e.getMessage());
+// } finally {
+// DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+// }
+// }
private static boolean deleteViaContentProvider(String namespace, String key) {
ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index b188dc3..4543019 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -50,9 +50,6 @@
@Singleton
public class FullscreenUserSwitcher {
private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
- // Because user 0 is headless, user count for single user is 2
- private static final int NUMBER_OF_BACKGROUND_USERS = 1;
-
private final Context mContext;
private final Resources mResources;
private final UserManager mUserManager;
@@ -137,27 +134,18 @@
/* isStartGuestSession= */ false,
/* isAddUser= */ false,
/* isForeground= */ true);
- // For single user without trusted device, hide the user switcher.
- if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) {
- dismissUserSwitcher();
- return;
- }
- // Show unlock dialog for initial user
+
+ // If the initial user has trusted device, display the unlock dialog on the keyguard.
if (hasTrustedDevice(initialUser)) {
mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
mOnHideListener);
+ } else {
+ // If no trusted device, dismiss the keyguard.
+ dismissUserSwitcher();
}
}
/**
- * Check if there is only one possible user to login in.
- * In a Multi-User system there is always one background user (user 0)
- */
- private boolean hasMultipleUsers() {
- return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1;
- }
-
- /**
* Makes user grid visible.
*/
public void show() {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f5d1ccf..0959de9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -101,6 +101,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -383,6 +384,13 @@
break;
}
+ case Settings.CALL_METHOD_SET_ALL_CONFIG: {
+ String prefix = getSettingPrefix(args);
+ Map<String, String> flags = getSettingFlags(args);
+ setAllConfigSettings(prefix, flags);
+ break;
+ }
+
case Settings.CALL_METHOD_RESET_CONFIG: {
final int mode = getResetModeEnforcingPermission(args);
String prefix = getSettingPrefix(args);
@@ -1030,6 +1038,19 @@
MUTATION_OPERATION_INSERT, 0);
}
+ private boolean setAllConfigSettings(String prefix, Map<String, String> keyValues) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "setAllConfigSettings for prefix: " + prefix);
+ }
+
+ enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+
+ synchronized (mLock) {
+ return mSettingsRegistry.setSettingsLocked(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM,
+ prefix, keyValues, resolveCallingPackage());
+ }
+ }
+
private boolean deleteConfigSetting(String name) {
if (DEBUG) {
Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ")");
@@ -2117,6 +2138,11 @@
return (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
}
+ private static Map<String, String> getSettingFlags(Bundle args) {
+ return (args != null) ? (HashMap) args.getSerializable(Settings.CALL_METHOD_FLAGS_KEY)
+ : Collections.emptyMap();
+ }
+
private static boolean getSettingMakeDefault(Bundle args) {
return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY);
}
@@ -2485,7 +2511,7 @@
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState == null) {
- return new ArrayList<String>();
+ return new ArrayList<>();
}
return settingsState.getSettingNamesLocked();
}
@@ -2645,6 +2671,22 @@
return success;
}
+ public boolean setSettingsLocked(int type, int userId, String prefix,
+ Map<String, String> keyValues, String packageName) {
+ final int key = makeKey(type, userId);
+
+ SettingsState settingsState = peekSettingsStateLocked(key);
+ if (settingsState != null) {
+ List<String> changedSettings =
+ settingsState.setSettingsLocked(prefix, keyValues, packageName);
+ if (!changedSettings.isEmpty()) {
+ notifyForConfigSettingsChangeLocked(key, prefix, changedSettings);
+ }
+ }
+
+ return settingsState != null;
+ }
+
public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
Set<String> criticalSettings) {
final int key = makeKey(type, userId);
@@ -3043,6 +3085,28 @@
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
}
+ private void notifyForConfigSettingsChangeLocked(int key, String prefix,
+ List<String> changedSettings) {
+
+ // Increment the generation first, so observers always see the new value
+ mGenerationRegistry.incrementGeneration(key);
+
+ StringBuilder stringBuilder = new StringBuilder(prefix);
+ for (int i = 0; i < changedSettings.size(); ++i) {
+ stringBuilder.append(changedSettings.get(i).split("/")[1]).append("/");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ notifySettingChangeForRunningUsers(key, stringBuilder.toString());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // Always notify that our data changed
+ mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
+ }
+
private void maybeNotifyProfiles(int type, int userId, Uri uri, String name,
Collection<String> keysCloned) {
if (keysCloned.contains(name)) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index de6a3a8..4731e68 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -64,6 +64,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -416,6 +417,57 @@
}
// The settings provider must hold its lock when calling here.
+ // Returns the list of keys which changed (added, updated, or deleted).
+ @GuardedBy("mLock")
+ public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues,
+ String packageName) {
+ List<String> changedKeys = new ArrayList<>();
+ // Delete old keys with the prefix that are not part of the new set.
+ for (int i = 0; i < mSettings.keySet().size(); ++i) {
+ String key = mSettings.keyAt(i);
+ if (key.startsWith(prefix) && !keyValues.containsKey(key)) {
+ Setting oldState = mSettings.remove(key);
+
+ StatsLog.write(StatsLog.SETTING_CHANGED, key, /* value= */ "", /* newValue= */ "",
+ oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
+ StatsLog.SETTING_CHANGED__REASON__DELETED);
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
+ changedKeys.add(key); // key was removed
+ }
+ }
+
+ // Update/add new keys
+ for (String key : keyValues.keySet()) {
+ String value = keyValues.get(key);
+ String oldValue = null;
+ Setting state = mSettings.get(key);
+ if (state == null) {
+ state = new Setting(key, value, false, packageName, null);
+ mSettings.put(key, state);
+ changedKeys.add(key); // key was added
+ } else if (state.value != value) {
+ oldValue = state.value;
+ state.update(value, false, packageName, null, true);
+ changedKeys.add(key); // key was updated
+ } else {
+ // this key/value already exists, no change and no logging necessary
+ continue;
+ }
+
+ StatsLog.write(StatsLog.SETTING_CHANGED, key, value, state.value, oldValue,
+ /* tag */ null, /* make default */ false,
+ getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, state);
+ }
+
+ if (!changedKeys.isEmpty()) {
+ scheduleWriteIfNeededLocked();
+ }
+
+ return changedKeys;
+ }
+
+ // The settings provider must hold its lock when calling here.
public void persistSyncLocked() {
mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
doWriteState();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index dcc690f..4a925aa 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2771,11 +2771,6 @@
*/
@Override
public boolean supportsCheckpoint() throws RemoteException {
- // Only the system process is permitted to start checkpoints
- if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
- throw new SecurityException("no permission to check filesystem checkpoint support");
- }
-
return mVold.supportsCheckpoint();
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3390ca7..5bb42d4 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -45,6 +45,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.storage.StorageManager;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -75,6 +76,7 @@
private final PackageInstallerService mPi;
private final ApexManager mApexManager;
private final PowerManager mPowerManager;
+ private final Context mContext;
private final PreRebootVerificationHandler mPreRebootVerificationHandler;
@GuardedBy("mStagedSessions")
@@ -83,6 +85,7 @@
StagingManager(PackageInstallerService pi, ApexManager am, Context context) {
mPi = pi;
mApexManager = am;
+ mContext = context;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPreRebootVerificationHandler = new PreRebootVerificationHandler(
BackgroundThread.get().getLooper());
@@ -539,6 +542,10 @@
mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
}
+ private int parentOrOwnSessionId(PackageInstallerSession session) {
+ return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
+ }
+
/**
* <p> Check if the session provided is non-overlapping with the active staged sessions.
*
@@ -561,6 +568,9 @@
"Cannot stage session " + session.sessionId + " with package name null");
}
+ boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
+ Context.STORAGE_SERVICE)).isCheckpointSupported();
+
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -601,7 +611,17 @@
+ stagedSession.sessionId, null);
}
- // TODO(b/141843321): Add support for staging multiple sessions in apexd
+ // Staging multiple root sessions is not allowed if device doesn't support
+ // checkpoint. If session and stagedSession do not have common ancestor, they are
+ // from two different root sessions.
+ if (!supportsCheckpoint
+ && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+ "Cannot stage multiple sessions without checkpoint support", null);
+ }
+
+ // TODO:b/141843321 Add support for staging multiple sessions in apexd
// Since apexd doesn't support multiple staged sessions yet, we have to careful how
// we handle apex sessions. We want to allow a set of apex sessions under the same
// parent to be staged when there is no previously staged apex sessions.
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 15bb002..add0316 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -4090,9 +4090,7 @@
/**
* The subscription which received this cell broadcast message.
- * @deprecated use {@link #SLOT_INDEX} instead.
* <P>Type: INTEGER</P>
- * @hide
*/
public static final String SUB_ID = "sub_id";
@@ -4366,6 +4364,7 @@
public static final String[] QUERY_COLUMNS_FWK = {
_ID,
SLOT_INDEX,
+ SUB_ID,
GEOGRAPHICAL_SCOPE,
PLMN,
LAC,
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 737ead1..fad70d2 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -205,7 +205,9 @@
/** CMAS warning area coordinates. */
private final List<Geometry> mGeometries;
- private int mSlotIndex = 0;
+ private final int mSlotIndex;
+
+ private final int mSubId;
/**
* Create a new SmsCbMessage with the specified data.
@@ -214,11 +216,11 @@
public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
@NonNull SmsCbLocation location, int serviceCategory, @Nullable String language,
@Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
- @Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex) {
+ @Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex, int subId) {
this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
- null /* geometries */, System.currentTimeMillis(), slotIndex);
+ null /* geometries */, System.currentTimeMillis(), slotIndex, subId);
}
/**
@@ -226,10 +228,12 @@
* coordinates information.
*/
public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
- @NonNull SmsCbLocation location, int serviceCategory, @Nullable String language,
- @Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
- @Nullable SmsCbCmasInfo cmasWarningInfo, int maximumWaitTimeSec,
- @Nullable List<Geometry> geometries, long receivedTimeMillis, int slotIndex) {
+ @NonNull SmsCbLocation location, int serviceCategory,
+ @Nullable String language, @Nullable String body, int priority,
+ @Nullable SmsCbEtwsInfo etwsWarningInfo,
+ @Nullable SmsCbCmasInfo cmasWarningInfo, int maximumWaitTimeSec,
+ @Nullable List<Geometry> geometries, long receivedTimeMillis, int slotIndex,
+ int subId) {
mMessageFormat = messageFormat;
mGeographicalScope = geographicalScope;
mSerialNumber = serialNumber;
@@ -244,6 +248,7 @@
mGeometries = geometries;
mMaximumWaitTimeSec = maximumWaitTimeSec;
mSlotIndex = slotIndex;
+ mSubId = subId;
}
/**
@@ -282,6 +287,7 @@
mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
mMaximumWaitTimeSec = in.readInt();
mSlotIndex = in.readInt();
+ mSubId = in.readInt();
}
/**
@@ -317,6 +323,7 @@
mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
dest.writeInt(mMaximumWaitTimeSec);
dest.writeInt(mSlotIndex);
+ dest.writeInt(mSubId);
}
@NonNull
@@ -427,14 +434,22 @@
}
/**
- * Get the slotIndex associated with this message.
- * @return the slotIndex associated with this message
+ * Get the slot index associated with this message.
+ * @return the slot index associated with this message
*/
public int getSlotIndex() {
return mSlotIndex;
}
/**
+ * Get the subscription id associated with this message.
+ * @return the subscription id associated with this message
+ */
+ public int getSubscriptionId() {
+ return mSubId;
+ }
+
+ /**
* Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
* @return an integer representing 3GPP or 3GPP2 message format
*/
@@ -536,6 +551,7 @@
public ContentValues getContentValues() {
ContentValues cv = new ContentValues(16);
cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
+ cv.put(CellBroadcasts.SUB_ID, mSubId);
cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
if (mLocation.getPlmn() != null) {
cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
@@ -577,7 +593,6 @@
}
cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec);
- cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
return cv;
}
@@ -600,6 +615,7 @@
int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX));
+ int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUB_ID));
String plmn;
int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
@@ -697,7 +713,7 @@
return new SmsCbMessage(format, geoScope, serialNum, location, category,
language, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
- receivedTimeMillis, slotIndex);
+ receivedTimeMillis, slotIndex, subId);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 6fc0228..1f7715b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -875,9 +875,10 @@
* Parses a broadcast SMS, possibly containing a CMAS alert.
*
* @param plmn the PLMN for a broadcast SMS
- * @param subId
+ * @param slotIndex SIM slot index
+ * @param subId Subscription id
*/
- public SmsCbMessage parseBroadcastSms(String plmn, int subId) {
+ public SmsCbMessage parseBroadcastSms(String plmn, int slotIndex, int subId) {
BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
if (bData == null) {
Rlog.w(LOG_TAG, "BearerData.decode() returned null");
@@ -893,7 +894,7 @@
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location,
mEnvelope.serviceCategory, bData.getLanguage(), bData.userData.payloadStr,
- bData.priority, null, bData.cmasWarningInfo, subId);
+ bData.priority, null, bData.cmasWarningInfo, slotIndex, subId);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index d03419d..c16eafb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -33,6 +33,7 @@
import android.telephony.Rlog;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
+import android.telephony.SubscriptionManager;
import android.util.Pair;
import com.android.internal.R;
@@ -95,6 +96,14 @@
public static SmsCbMessage createSmsCbMessage(Context context, SmsCbHeader header,
SmsCbLocation location, byte[][] pdus, int slotIndex)
throws IllegalArgumentException {
+ SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ int[] subIds = sm.getSubscriptionIds(slotIndex);
+ if (subIds != null && subIds.length > 0) {
+ subId = subIds[0];
+ }
+
long receivedTimeMillis = System.currentTimeMillis();
if (header.isEtwsPrimaryNotification()) {
// ETSI TS 23.041 ETWS Primary Notification message
@@ -105,7 +114,8 @@
header.getSerialNumber(), location, header.getServiceCategory(), null,
getEtwsPrimaryMessage(context, header.getEtwsInfo().getWarningType()),
SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, header.getEtwsInfo(),
- header.getCmasInfo(), 0, null /* geometries */, receivedTimeMillis, slotIndex);
+ header.getCmasInfo(), 0, null /* geometries */, receivedTimeMillis, slotIndex,
+ subId);
} else if (header.isUmtsFormat()) {
// UMTS format has only 1 PDU
byte[] pdu = pdus[0];
@@ -139,7 +149,7 @@
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, body, priority,
header.getEtwsInfo(), header.getCmasInfo(), maximumWaitingTimeSec, geometries,
- receivedTimeMillis, slotIndex);
+ receivedTimeMillis, slotIndex, subId);
} else {
String language = null;
StringBuilder sb = new StringBuilder();
@@ -155,7 +165,7 @@
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, sb.toString(), priority,
header.getEtwsInfo(), header.getCmasInfo(), 0, null /* geometries */,
- receivedTimeMillis, slotIndex);
+ receivedTimeMillis, slotIndex, subId);
}
}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 6a03c73..73c52ab 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -1212,7 +1212,7 @@
*
* @hide
*/
- public RttManager(Context context, WifiRttManager service) {
+ public RttManager(@NonNull Context context, @NonNull WifiRttManager service) {
mNewService = service;
mContext = context;
diff --git a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
new file mode 100644
index 0000000..775043a
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 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.net.wifi;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.net.wifi.aware.IWifiAwareManager;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.p2p.IWifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.rtt.IWifiRttManager;
+import android.net.wifi.rtt.WifiRttManager;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+/**
+ * Class for performing registration for all Wifi services.
+ *
+ * @hide
+ */
+@SystemApi
+public class WifiFrameworkInitializer {
+
+ /**
+ * A class implementing the lazy holder idiom: the unique static instance
+ * of {@link #INSTANCE} is instantiated in a thread-safe way (guaranteed by
+ * the language specs) the first time that NoPreloadHolder is referenced in getInstanceLooper().
+ *
+ * This is necessary because we can't spawn a new thread in {@link #registerServiceWrappers()}.
+ * {@link #registerServiceWrappers()} is called during the Zygote phase, which disallows
+ * spawning new threads. Naming the class "NoPreloadHolder" ensures that the classloader will
+ * not preload this class, inadvertently spawning the thread too early.
+ */
+ private static class NoPreloadHolder {
+ private static final HandlerThread INSTANCE = createInstance();
+
+ private static HandlerThread createInstance() {
+ HandlerThread thread = new HandlerThread("WifiManagerThread");
+ thread.start();
+ return thread;
+ }
+ }
+
+ private static Looper getInstanceLooper() {
+ return NoPreloadHolder.INSTANCE.getLooper();
+ }
+
+ private WifiFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all Wifi services
+ * to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides
+ * {@link SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_SERVICE,
+ WifiManager.class,
+ context -> new WifiManager(context, getInstanceLooper())
+ );
+ SystemServiceRegistry.registerStaticService(
+ Context.WIFI_P2P_SERVICE,
+ WifiP2pManager.class,
+ serviceBinder -> {
+ IWifiP2pManager service = IWifiP2pManager.Stub.asInterface(serviceBinder);
+ return new WifiP2pManager(service);
+ }
+ );
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_AWARE_SERVICE,
+ WifiAwareManager.class,
+ (context, serviceBinder) -> {
+ IWifiAwareManager service = IWifiAwareManager.Stub.asInterface(serviceBinder);
+ return new WifiAwareManager(context, service);
+ }
+ );
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_SCANNING_SERVICE,
+ WifiScanner.class,
+ (context, serviceBinder) -> {
+ IWifiScanner service = IWifiScanner.Stub.asInterface(serviceBinder);
+ return new WifiScanner(context, service, getInstanceLooper());
+ }
+ );
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_RTT_SERVICE,
+ RttManager.class,
+ (context, serviceBinder) -> {
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(serviceBinder);
+ WifiRttManager wifiRttManager = new WifiRttManager(context, service);
+ return new RttManager(context, wifiRttManager);
+ }
+ );
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_RTT_RANGING_SERVICE,
+ WifiRttManager.class,
+ (context, serviceBinder) -> {
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(serviceBinder);
+ return new WifiRttManager(context, service);
+ }
+ );
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 77666b0..942d795 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1213,11 +1213,13 @@
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
+ *
* @param context the application context
- * @hide - hide this because it takes in a parameter of type IWifiManager, which
- * is a system private class.
+ * @param looper the Looper used to deliver callbacks
+ *
+ * @hide
*/
- public WifiManager(Context context, Looper looper) {
+ public WifiManager(@NonNull Context context, @NonNull Looper looper) {
mContext = context;
mLooper = looper;
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 0d36718..771f3b8 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1291,12 +1291,15 @@
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
+ *
* @param context the application context
- * @param service the Binder interface
+ * @param service the Binder interface for {@link Context#WIFI_SCANNING_SERVICE}
* @param looper the Looper used to deliver callbacks
+ *
* @hide
*/
- public WifiScanner(Context context, IWifiScanner service, Looper looper) {
+ public WifiScanner(@NonNull Context context, @NonNull IWifiScanner service,
+ @NonNull Looper looper) {
mContext = context;
mService = service;
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 5aab347..7b37d65 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -158,7 +158,7 @@
private final Object mLock = new Object(); // lock access to the following vars
/** @hide */
- public WifiAwareManager(Context context, IWifiAwareManager service) {
+ public WifiAwareManager(@NonNull Context context, @NonNull IWifiAwareManager service) {
mContext = context;
mService = service;
}
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index 770a120..cb0c5d4 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -77,7 +77,7 @@
"android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED";
/** @hide */
- public WifiRttManager(Context context, IWifiRttManager service) {
+ public WifiRttManager(@NonNull Context context, @NonNull IWifiRttManager service) {
mContext = context;
mService = service;
}