Merge "System UserInfo flags are set in UserTypeFactory" into udc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 4bddbd6..4caaa09 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -4589,6 +4589,22 @@
Binder.restoreCallingIdentity(token);
}
}
+ } else if ("force-active".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ synchronized (this) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mForceIdle = true;
+ becomeActiveLocked("force-active", Process.myUid());
+ pw.print("Light state: ");
+ pw.print(lightStateToString(mLightState));
+ pw.print(", deep state: ");
+ pw.println(stateToString(mState));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else if ("force-idle".equals(cmd)) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
diff --git a/core/api/current.txt b/core/api/current.txt
index eef16be..651669c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -41246,6 +41246,7 @@
method public void setUiEnabled(boolean);
method public void show(android.os.Bundle, int);
method public void startAssistantActivity(android.content.Intent);
+ method public void startAssistantActivity(@NonNull android.content.Intent, @NonNull android.os.Bundle);
method public void startVoiceActivity(android.content.Intent);
method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6581c42..95e331e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10152,7 +10152,6 @@
public abstract class SharedConnectivityService extends android.app.Service {
ctor public SharedConnectivityService();
- ctor public SharedConnectivityService(@NonNull android.os.Handler);
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
method public abstract void onConnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index f693a2f..eeff6cc 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -66,7 +66,7 @@
public final class DisplayManager {
private static final String TAG = "DisplayManager";
private static final boolean DEBUG = false;
- private static final boolean ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE = false;
+ private static final boolean ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE = true;
private final Context mContext;
private final DisplayManagerGlobal mGlobal;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 5778518..cabcae3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.Dialog;
import android.app.DirectAction;
import android.app.Instrumentation;
@@ -1527,8 +1528,34 @@
* <p>By default, the system will create a window for the UI for this session. If you are using
* an assistant activity instead, then you can disable the window creation by calling
* {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+ *
+ * NOTE: if the app would like to override some options to start the Activity,
+ * use {@link #startAssistantActivity(Intent, Bundle)} instead.
*/
public void startAssistantActivity(Intent intent) {
+ startAssistantActivity(intent, ActivityOptions.makeBasic().toBundle());
+ }
+
+ /**
+ * <p>Ask that a new assistant activity be started. This will create a new task in the
+ * in activity manager: this means that
+ * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+ * will be set for you to make it a new task.</p>
+ *
+ * <p>The newly started activity will be displayed on top of other activities in the system
+ * in a new layer that is not affected by multi-window mode. Tasks started from this activity
+ * will go into the normal activity layer and not this new layer.</p>
+ *
+ * <p>By default, the system will create a window for the UI for this session. If you are using
+ * an assistant activity instead, then you can disable the window creation by calling
+ * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+ *
+ * @param intent the intent used to start an assistant activity
+ * @param bundle Additional options for how the Activity should be started. See
+ * {@link ActivityOptions} for how to build the Bundle supplied here.
+ */
+ public void startAssistantActivity(@NonNull Intent intent, @NonNull Bundle bundle) {
+ Objects.requireNonNull(bundle);
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
@@ -1537,7 +1564,7 @@
intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startAssistantActivity(mToken, intent,
intent.resolveType(mContext.getContentResolver()),
- mContext.getAttributionTag());
+ mContext.getAttributionTag(), bundle);
Instrumentation.checkStartActivityResult(res, intent);
} catch (RemoteException e) {
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 5eb9786..6b40d98 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -51,7 +51,7 @@
int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
String attributionTag);
int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
- String attributionTag);
+ String attributionTag, in Bundle bundle);
void setKeepAwake(IBinder token, boolean keepAwake);
void closeSystemDialogs(IBinder token);
void finish(IBinder token);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index d726b67..71050fa 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -506,6 +506,8 @@
<permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
<!-- Permission required for CTS test - CtsAmbientContextServiceTestCases -->
<permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"/>
+ <!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
+ <permission name="android.permission.WRITE_APN_SETTINGS"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 8f2e7b5..4676dff 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -224,6 +224,10 @@
/**
* Gets audio presentations.
+ *
+ * <p>The audio presentation order matters. As specified in ETSI EN 300 468 V1.17.1, all the
+ * audio programme components corresponding to the first audio preselection in the loop are
+ * contained in the main NGA stream.
*/
@NonNull
public List<AudioPresentation> getAudioPresentations() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0f67124..82ca63d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -819,6 +819,9 @@
<!-- Permission required for CTS test - ActivityCaptureCallbackTests -->
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
+ <!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
+ <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index f712629..2b6327f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -16,7 +16,7 @@
package com.android.systemui.shade;
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
import static android.view.WindowInsets.Type.systemBars;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -328,7 +328,7 @@
@Override
public void requestLayout() {
- Trace.instant(TRACE_TAG_ALWAYS, "NotificationShadeWindowView#requestLayout");
+ Trace.instant(TRACE_TAG_APP, "NotificationShadeWindowView#requestLayout");
super.requestLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 32b8e09..c9f31ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -166,6 +166,11 @@
private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+ private final Runnable mRebuildListRunnable = () -> {
+ if (mBuildListener != null) {
+ mBuildListener.onBuildList(mReadOnlyNotificationSet, "asynchronousUpdate");
+ }
+ };
private boolean mAttached = false;
private boolean mAmDispatchingToOtherCode;
@@ -462,7 +467,7 @@
int modificationType) {
Assert.isMainThread();
mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
- dispatchEventsAndRebuildList("onNotificationChannelModified");
+ dispatchEventsAndAsynchronouslyRebuildList();
}
private void onNotificationsInitialized() {
@@ -621,15 +626,39 @@
private void dispatchEventsAndRebuildList(String reason) {
Trace.beginSection("NotifCollection.dispatchEventsAndRebuildList");
+ if (mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+ mMainHandler.removeCallbacks(mRebuildListRunnable);
+ }
+
+ dispatchEvents();
+
+ if (mBuildListener != null) {
+ mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
+ }
+ Trace.endSection();
+ }
+
+ private void dispatchEventsAndAsynchronouslyRebuildList() {
+ Trace.beginSection("NotifCollection.dispatchEventsAndAsynchronouslyRebuildList");
+
+ dispatchEvents();
+
+ if (!mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+ mMainHandler.postDelayed(mRebuildListRunnable, 1000L);
+ }
+
+ Trace.endSection();
+ }
+
+ private void dispatchEvents() {
+ Trace.beginSection("NotifCollection.dispatchEvents");
+
mAmDispatchingToOtherCode = true;
while (!mEventQueue.isEmpty()) {
mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
}
mAmDispatchingToOtherCode = false;
- if (mBuildListener != null) {
- mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
- }
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1fb7eb5..d2087ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
@@ -1121,7 +1121,7 @@
@Override
public void requestLayout() {
- Trace.instant(TRACE_TAG_ALWAYS, "NotificationStackScrollLayout#requestLayout");
+ Trace.instant(TRACE_TAG_APP, "NotificationStackScrollLayout#requestLayout");
super.requestLayout();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 005c80a..540bda6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -106,6 +106,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
import java.util.Arrays;
import java.util.Collection;
@@ -378,6 +379,90 @@
}
@Test
+ public void testScheduleBuildNotificationListWhenChannelChanged() {
+ // GIVEN
+ final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+ final NotificationChannel channel = new NotificationChannel(
+ "channelId",
+ "channelName",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ neb.setChannel(channel);
+
+ final NotifEvent notif = mNoMan.postNotif(neb);
+ final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+
+ clearInvocations(mBuildListener);
+
+ // WHEN
+ mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+ entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ // THEN
+ verify(mMainHandler).postDelayed(any(), eq(1000L));
+ }
+
+ @Test
+ public void testCancelScheduledBuildNotificationListEventWhenNotifUpdatedSynchronously() {
+ // GIVEN
+ final NotificationEntry entry1 = buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, "group_1")
+ .build();
+ final NotificationEntry entry2 = buildNotif(TEST_PACKAGE, 2)
+ .setGroup(mContext, "group_1")
+ .setContentTitle(mContext, "New version")
+ .build();
+ final NotificationEntry entry3 = buildNotif(TEST_PACKAGE, 3)
+ .setGroup(mContext, "group_1")
+ .build();
+
+ final List<CoalescedEvent> entriesToBePosted = Arrays.asList(
+ new CoalescedEvent(entry1.getKey(), 0, entry1.getSbn(), entry1.getRanking(), null),
+ new CoalescedEvent(entry2.getKey(), 1, entry2.getSbn(), entry2.getRanking(), null),
+ new CoalescedEvent(entry3.getKey(), 2, entry3.getSbn(), entry3.getRanking(), null)
+ );
+
+ when(mMainHandler.hasCallbacks(any())).thenReturn(true);
+
+ // WHEN
+ mNotifHandler.onNotificationBatchPosted(entriesToBePosted);
+
+ // THEN
+ verify(mMainHandler).removeCallbacks(any());
+ }
+
+ @Test
+ public void testBuildNotificationListWhenChannelChanged() {
+ // GIVEN
+ final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+ final NotificationChannel channel = new NotificationChannel(
+ "channelId",
+ "channelName",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ neb.setChannel(channel);
+
+ final NotifEvent notif = mNoMan.postNotif(neb);
+ final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+ when(mMainHandler.postDelayed(any(), eq(1000L))).thenAnswer((Answer) invocation -> {
+ final Runnable runnable = invocation.getArgument(0);
+ runnable.run();
+ return null;
+ });
+
+ clearInvocations(mBuildListener);
+
+ // WHEN
+ mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+ entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ // THEN
+ verifyBuiltList(List.of(entry));
+ }
+
+ @Test
public void testRankingsAreUpdatedForOtherNotifs() {
// GIVEN a collection with one notif
NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
index f674a7d..163f614 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -27,6 +27,7 @@
import android.app.role.RoleManager;
import android.companion.AssociationInfo;
import android.content.Context;
+import android.os.Binder;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -84,7 +85,9 @@
Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
+ ", package=u" + userId + "\\" + packageName);
- roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
success -> {
if (!success) {
@@ -92,6 +95,9 @@
+ " from the list of " + deviceProfile + " holders.");
}
});
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
private RolesUtils() {};
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index b001f3d..3304fb0 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1263,6 +1263,10 @@
sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
}
+ /*package*/ void setLeAudioTimeout(String address, int device, int delayMs) {
+ sendILMsg(MSG_IL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, address, delayMs);
+ }
+
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
synchronized (mDeviceStateLock) {
mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
@@ -1467,6 +1471,13 @@
mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
}
break;
+ case MSG_IL_BTLEAUDIO_TIMEOUT:
+ // msg.obj == address of LE Audio device
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onMakeLeAudioDeviceUnavailableNow(
+ (String) msg.obj, msg.arg1);
+ }
+ break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
synchronized (mDeviceStateLock) {
@@ -1703,12 +1714,14 @@
private static final int MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY = 47;
private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
+ private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_IL_BTA2DP_TIMEOUT:
+ case MSG_IL_BTLEAUDIO_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1800,6 +1813,7 @@
case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_IL_BTA2DP_TIMEOUT:
+ case MSG_IL_BTLEAUDIO_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
if (sLastDeviceConnectMsgTime >= time) {
// add a little delay to make sure messages are ordered as expected
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index f9270c9..aae1d38 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -395,7 +395,7 @@
case BluetoothProfile.LE_AUDIO:
case BluetoothProfile.LE_AUDIO_BROADCAST:
if (switchToUnavailable) {
- makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
+ makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
} else if (switchToAvailable) {
makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
@@ -507,6 +507,12 @@
}
}
+ /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device) {
+ synchronized (mDevicesLock) {
+ makeLeAudioDeviceUnavailableNow(address, device);
+ }
+ }
+
/*package*/ void onReportNewRoutes() {
int n = mRoutesObservers.beginBroadcast();
if (n > 0) {
@@ -1027,10 +1033,11 @@
new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
.record();
if (toRemove.size() > 0) {
- final int delay = checkSendBecomingNoisyIntentInt(device, 0,
+ final int delay = checkSendBecomingNoisyIntentInt(device,
+ AudioService.CONNECTION_STATE_DISCONNECTED,
AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
- makeLeAudioDeviceUnavailable(deviceAddress, device)
+ makeLeAudioDeviceUnavailableLater(deviceAddress, device, delay)
);
}
}
@@ -1331,9 +1338,21 @@
*/
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
- AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
+ final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ device, address, name),
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available LE Audio device addr=" + address
+ + " error=" + res).printLog(TAG));
+ // TODO: connection failed, stop here
+ // TODO: return;
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "LE Audio device addr=" + address + " now available").printLog(TAG));
+ }
+
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
@@ -1354,11 +1373,23 @@
}
@GuardedBy("mDevicesLock")
- private void makeLeAudioDeviceUnavailable(String address, int device) {
+ private void makeLeAudioDeviceUnavailableNow(String address, int device) {
if (device != AudioSystem.DEVICE_NONE) {
- AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address),
+ final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ device, address),
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make unavailable LE Audio device addr=" + address
+ + " error=" + res).printLog(TAG));
+ // TODO: failed to disconnect, stop here
+ // TODO: return;
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "LE Audio device addr=" + address + " made unavailable").printLog(TAG));
+ }
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
@@ -1366,6 +1397,14 @@
}
@GuardedBy("mDevicesLock")
+ private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
+ // the device will be made unavailable later, so consider it disconnected right away
+ mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
+ // send the delayed message to make the device unavailable later
+ mDeviceBroker.setLeAudioTimeout(address, device, delayMs);
+ }
+
+ @GuardedBy("mDevicesLock")
private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
synchronized (mCurAudioRoutes) {
if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index e7ad5b9..0804769 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -20,7 +20,9 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.util.IntArray;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -37,9 +39,7 @@
import java.util.List;
/**
- * Data structure that contains the mapping of users to user restrictions (either the user
- * restrictions that apply to them, or the user restrictions that they set, depending on the
- * circumstances).
+ * Data structure that contains the mapping of users to user restrictions.
*
* @hide
*/
@@ -88,6 +88,24 @@
}
/**
+ * Removes a particular restriction for all users.
+ *
+ * @return whether the restriction was removed or not.
+ */
+ public boolean removeRestrictionsForAllUsers(String restriction) {
+ boolean removed = false;
+ for (int i = 0; i < mUserRestrictions.size(); i++) {
+ final Bundle restrictions = mUserRestrictions.valueAt(i);
+
+ if (UserRestrictionsUtils.contains(restrictions, restriction)) {
+ restrictions.remove(restriction);
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
+ /**
* Moves a particular restriction from one restriction set to another, e.g. for all users.
*/
public void moveRestriction(@NonNull RestrictionsSet destRestrictions, String restriction) {
@@ -139,22 +157,19 @@
* @return list of enforcing users that enforce a particular restriction.
*/
public @NonNull List<UserManager.EnforcingUser> getEnforcingUsers(String restriction,
- @UserIdInt int deviceOwnerUserId) {
+ @UserIdInt int userId) {
final List<UserManager.EnforcingUser> result = new ArrayList<>();
- for (int i = 0; i < mUserRestrictions.size(); i++) {
- if (UserRestrictionsUtils.contains(mUserRestrictions.valueAt(i), restriction)) {
- result.add(getEnforcingUser(mUserRestrictions.keyAt(i), deviceOwnerUserId));
- }
+ if (getRestrictionsNonNull(userId).containsKey(restriction)) {
+ result.add(new UserManager.EnforcingUser(userId,
+ UserManager.RESTRICTION_SOURCE_PROFILE_OWNER));
}
- return result;
- }
- private UserManager.EnforcingUser getEnforcingUser(@UserIdInt int userId,
- @UserIdInt int deviceOwnerUserId) {
- int source = deviceOwnerUserId == userId
- ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER
- : UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
- return new UserManager.EnforcingUser(userId, source);
+ if (getRestrictionsNonNull(UserHandle.USER_ALL).containsKey(restriction)) {
+ result.add(new UserManager.EnforcingUser(UserHandle.USER_ALL,
+ UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
+ }
+
+ return result;
}
/**
@@ -165,6 +180,11 @@
return mUserRestrictions.get(userId);
}
+ /** @return list of user restrictions for a given user that is not null. */
+ public @NonNull Bundle getRestrictionsNonNull(@UserIdInt int userId) {
+ return UserRestrictionsUtils.nonNull(mUserRestrictions.get(userId));
+ }
+
/**
* Removes a given user from the restrictions set, returning true if the user has non-empty
* restrictions before removal.
@@ -236,6 +256,15 @@
}
}
+ /** @return list of users in this restriction set. */
+ public IntArray getUserIds() {
+ IntArray userIds = new IntArray(mUserRestrictions.size());
+ for (int i = 0; i < mUserRestrictions.size(); i++) {
+ userIds.add(mUserRestrictions.keyAt(i));
+ }
+ return userIds;
+ }
+
public boolean containsKey(@UserIdInt int userId) {
return mUserRestrictions.contains(userId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 36efc0d..9ef1bba 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -141,6 +141,18 @@
public abstract void setDevicePolicyUserRestrictions(int originatingUserId,
@Nullable Bundle global, @Nullable RestrictionsSet local, boolean isDeviceOwner);
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set a
+ * user restriction.
+ *
+ * @param userId user id to apply the restriction to. {@link com.android.os.UserHandle.USER_ALL}
+ * will apply the restriction to all users globally.
+ * @param key The key of the restriction.
+ * @param value The value of the restriction.
+ */
+ public abstract void setUserRestriction(@UserIdInt int userId, @NonNull String key,
+ boolean value);
+
/** Return a user restriction. */
public abstract boolean getUserRestriction(int userId, String key);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 079cc80..58ae955 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -457,30 +457,12 @@
/**
* User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
- * that should be applied to all users, including guests. Only non-empty restriction bundles are
- * stored.
- * The key is the user id of the user whom the restriction originated from.
- */
- @GuardedBy("mRestrictionsLock")
- private final RestrictionsSet mDevicePolicyGlobalUserRestrictions = new RestrictionsSet();
-
- /**
- * Id of the user that set global restrictions.
- */
- @GuardedBy("mRestrictionsLock")
- private int mDeviceOwnerUserId = UserHandle.USER_NULL;
-
- /**
- * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
- * for each user.
+ * for each user. Restrictions that apply to all users (global) are represented by
+ * {@link com.android.os.UserHandle.USER_ALL}.
* The key is the user id of the user whom the restrictions are targeting.
- * The key inside the restrictionsSet is the user id of the user whom the restriction
- * originated from.
- * targetUserId -> originatingUserId -> restrictionBundle
*/
@GuardedBy("mRestrictionsLock")
- private final SparseArray<RestrictionsSet> mDevicePolicyLocalUserRestrictions =
- new SparseArray<>();
+ private final RestrictionsSet mDevicePolicyUserRestrictions = new RestrictionsSet();
@GuardedBy("mGuestRestrictions")
private final Bundle mGuestRestrictions = new Bundle();
@@ -2567,150 +2549,69 @@
}
}
+ private void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ return;
+ }
+ synchronized (mRestrictionsLock) {
+ final Bundle newRestrictions = BundleUtils.clone(
+ mDevicePolicyUserRestrictions.getRestrictions(userId));
+ newRestrictions.putBoolean(key, value);
+
+ if (mDevicePolicyUserRestrictions.updateRestrictions(userId, newRestrictions)) {
+ if (userId == UserHandle.USER_ALL) {
+ applyUserRestrictionsForAllUsersLR();
+ } else {
+ applyUserRestrictionsLR(userId);
+ }
+ }
+ }
+ }
+
/**
* See {@link UserManagerInternal#setDevicePolicyUserRestrictions}
*/
private void setDevicePolicyUserRestrictionsInner(@UserIdInt int originatingUserId,
@NonNull Bundle global, @NonNull RestrictionsSet local,
boolean isDeviceOwner) {
- boolean globalChanged, localChanged;
- List<Integer> updatedLocalTargetUserIds;
synchronized (mRestrictionsLock) {
- // Update global and local restrictions if they were changed.
- globalChanged = mDevicePolicyGlobalUserRestrictions
- .updateRestrictions(originatingUserId, global);
- updatedLocalTargetUserIds = getUpdatedTargetUserIdsFromLocalRestrictions(
- originatingUserId, local);
- localChanged = updateLocalRestrictionsForTargetUsersLR(originatingUserId, local,
- updatedLocalTargetUserIds);
- if (isDeviceOwner) {
- // Remember the global restriction owner userId to be able to make a distinction
- // in getUserRestrictionSource on who set local policies.
- mDeviceOwnerUserId = originatingUserId;
- } else {
- if (mDeviceOwnerUserId == originatingUserId) {
- // When profile owner sets restrictions it passes null global bundle and we
- // reset global restriction owner userId.
- // This means this user used to have DO, but now the DO is gone and the user
- // instead has PO.
- mDeviceOwnerUserId = UserHandle.USER_NULL;
- }
- }
- }
- if (DBG) {
- Slog.d(LOG_TAG, "setDevicePolicyUserRestrictions: "
- + " originatingUserId=" + originatingUserId
- + " global=" + global + (globalChanged ? " (changed)" : "")
- + " local=" + local + (localChanged ? " (changed)" : "")
- );
- }
- // Don't call them within the mRestrictionsLock.
- synchronized (mPackagesLock) {
- if (globalChanged || localChanged) {
- if (updatedLocalTargetUserIds.size() == 1
- && updatedLocalTargetUserIds.contains(originatingUserId)) {
- writeUserLP(getUserDataNoChecks(originatingUserId));
- } else {
- if (globalChanged) {
- writeUserLP(getUserDataNoChecks(originatingUserId));
- }
- if (localChanged) {
- for (int targetUserId : updatedLocalTargetUserIds) {
- writeAllTargetUsersLP(targetUserId);
- }
- }
- }
- }
- }
+ final IntArray updatedUserIds = mDevicePolicyUserRestrictions.getUserIds();
- synchronized (mRestrictionsLock) {
- if (globalChanged) {
- applyUserRestrictionsForAllUsersLR();
- } else if (localChanged) {
- for (int targetUserId : updatedLocalTargetUserIds) {
- applyUserRestrictionsLR(targetUserId);
- }
- }
- }
- }
+ mCachedEffectiveUserRestrictions.removeAllRestrictions();
+ mDevicePolicyUserRestrictions.removeAllRestrictions();
- /**
- * @return the list of updated target user ids in device policy local restrictions for a
- * given originating user id.
- */
- private List<Integer> getUpdatedTargetUserIdsFromLocalRestrictions(int originatingUserId,
- @NonNull RestrictionsSet local) {
- List<Integer> targetUserIds = new ArrayList<>();
- // Update all the target user ids from the local restrictions set
- for (int i = 0; i < local.size(); i++) {
- targetUserIds.add(local.keyAt(i));
- }
- // Update the target user id from device policy local restrictions if the local
- // restrictions set does not contain the target user id.
- for (int i = 0; i < mDevicePolicyLocalUserRestrictions.size(); i++) {
- int targetUserId = mDevicePolicyLocalUserRestrictions.keyAt(i);
- RestrictionsSet restrictionsSet = mDevicePolicyLocalUserRestrictions.valueAt(i);
- if (!local.containsKey(targetUserId)
- && restrictionsSet.containsKey(originatingUserId)) {
- targetUserIds.add(targetUserId);
- }
- }
- return targetUserIds;
- }
+ mDevicePolicyUserRestrictions.updateRestrictions(UserHandle.USER_ALL, global);
- /**
- * Update restrictions for all target users in the restriction set. If a target user does not
- * exist in device policy local restrictions, remove the restrictions bundle for that target
- * user originating from the specified originating user.
- */
- @GuardedBy("mRestrictionsLock")
- private boolean updateLocalRestrictionsForTargetUsersLR(int originatingUserId,
- RestrictionsSet local, List<Integer> updatedTargetUserIds) {
- boolean changed = false;
- for (int targetUserId : updatedTargetUserIds) {
- Bundle restrictions = local.getRestrictions(targetUserId);
- if (restrictions == null) {
- restrictions = new Bundle();
+ final IntArray localUserIds = local.getUserIds();
+ for (int i = 0; i < localUserIds.size(); i++) {
+ final int userId = localUserIds.get(i);
+ mDevicePolicyUserRestrictions.updateRestrictions(userId,
+ local.getRestrictions(userId));
+ updatedUserIds.add(userId);
}
- if (getDevicePolicyLocalRestrictionsForTargetUserLR(targetUserId)
- .updateRestrictions(originatingUserId, restrictions)) {
- changed = true;
- }
- }
- return changed;
- }
- /**
- * A new restriction set is created if a restriction set does not already exist for a given
- * target user.
- *
- * @return restrictions set for a given target user.
- */
- @GuardedBy("mRestrictionsLock")
- private @NonNull RestrictionsSet getDevicePolicyLocalRestrictionsForTargetUserLR(
- int targetUserId) {
- RestrictionsSet result = mDevicePolicyLocalUserRestrictions.get(targetUserId);
- if (result == null) {
- result = new RestrictionsSet();
- mDevicePolicyLocalUserRestrictions.put(targetUserId, result);
+ applyUserRestrictionsForAllUsersLR();
+ for (int i = 0; i < updatedUserIds.size(); i++) {
+ applyUserRestrictionsLR(updatedUserIds.get(i));
+ }
}
- return result;
}
@GuardedBy("mRestrictionsLock")
private Bundle computeEffectiveUserRestrictionsLR(@UserIdInt int userId) {
- final Bundle baseRestrictions =
- UserRestrictionsUtils.nonNull(mBaseUserRestrictions.getRestrictions(userId));
- final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
- final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
+ final Bundle baseRestrictions = mBaseUserRestrictions.getRestrictionsNonNull(userId);
- if (BundleUtils.isEmpty(global) && local.isEmpty()) {
+ final Bundle global = mDevicePolicyUserRestrictions.getRestrictionsNonNull(
+ UserHandle.USER_ALL);
+ final Bundle local = mDevicePolicyUserRestrictions.getRestrictionsNonNull(userId);
+
+ if (global.isEmpty() && local.isEmpty()) {
// Common case first.
return baseRestrictions;
}
final Bundle effective = BundleUtils.clone(baseRestrictions);
UserRestrictionsUtils.merge(effective, global);
- UserRestrictionsUtils.merge(effective, local.mergeAll());
+ UserRestrictionsUtils.merge(effective, local);
return effective;
}
@@ -2834,13 +2735,7 @@
}
synchronized (mRestrictionsLock) {
- // Check if it is set as a local restriction.
- result.addAll(getDevicePolicyLocalRestrictionsForTargetUserLR(userId).getEnforcingUsers(
- restrictionKey, mDeviceOwnerUserId));
-
- // Check if it is set as a global restriction.
- result.addAll(mDevicePolicyGlobalUserRestrictions.getEnforcingUsers(restrictionKey,
- mDeviceOwnerUserId));
+ result.addAll(mDevicePolicyUserRestrictions.getEnforcingUsers(restrictionKey, userId));
}
return result;
}
@@ -2990,6 +2885,7 @@
@GuardedBy("mRestrictionsLock")
private void applyUserRestrictionsLR(@UserIdInt int userId) {
updateUserRestrictionsInternalLR(null, userId);
+ scheduleWriteUser(getUserDataNoChecks(userId));
}
@GuardedBy("mRestrictionsLock")
@@ -3666,10 +3562,6 @@
parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
}
- // Pre-O global user restriction were stored as a single bundle (as opposed to per-user
- // currently), take care of it in case of upgrade.
- Bundle oldDevicePolicyGlobalUserRestrictions = null;
-
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
final String name = parser.getName();
@@ -3698,23 +3590,12 @@
break;
}
}
- } else if (name.equals(TAG_DEVICE_OWNER_USER_ID)
- // Legacy name, should only be encountered when upgrading from pre-O.
- || name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
- synchronized (mRestrictionsLock) {
- mDeviceOwnerUserId =
- parser.getAttributeInt(null, ATTR_ID, mDeviceOwnerUserId);
- }
- } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) {
- // Should only happen when upgrading from pre-O (version < 7).
- oldDevicePolicyGlobalUserRestrictions =
- UserRestrictionsUtils.readRestrictions(parser);
}
}
}
updateUserIds();
- upgradeIfNecessaryLP(oldDevicePolicyGlobalUserRestrictions);
+ upgradeIfNecessaryLP();
} catch (IOException | XmlPullParserException e) {
fallbackToSingleUserLP();
} finally {
@@ -3724,21 +3605,19 @@
/**
* Upgrade steps between versions, either for fixing bugs or changing the data format.
- * @param oldGlobalUserRestrictions Pre-O global device policy restrictions.
*/
@GuardedBy({"mPackagesLock"})
- private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
- upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion);
+ private void upgradeIfNecessaryLP() {
+ upgradeIfNecessaryLP(mUserVersion, mUserTypeVersion);
}
/**
- * Version of {@link #upgradeIfNecessaryLP(Bundle)} that takes in the userVersion for testing
- * purposes. For non-tests, use {@link #upgradeIfNecessaryLP(Bundle)}.
+ * Version of {@link #upgradeIfNecessaryLP()} that takes in the userVersion for testing
+ * purposes. For non-tests, use {@link #upgradeIfNecessaryLP()}.
*/
@GuardedBy({"mPackagesLock"})
@VisibleForTesting
- void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion,
- int userTypeVersion) {
+ void upgradeIfNecessaryLP(int userVersion, int userTypeVersion) {
Slog.i(LOG_TAG, "Upgrading users from userVersion " + userVersion + " to " + USER_VERSION);
Set<Integer> userIdsToWrite = new ArraySet<>();
final int originalVersion = mUserVersion;
@@ -3792,16 +3671,11 @@
if (userVersion < 7) {
// Previously only one user could enforce global restrictions, now it is per-user.
synchronized (mRestrictionsLock) {
- if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
- && mDeviceOwnerUserId != UserHandle.USER_NULL) {
- mDevicePolicyGlobalUserRestrictions.updateRestrictions(
- mDeviceOwnerUserId, oldGlobalUserRestrictions);
+ if (mDevicePolicyUserRestrictions.removeRestrictionsForAllUsers(
+ UserManager.ENSURE_VERIFY_APPS)) {
+ mDevicePolicyUserRestrictions.getRestrictionsNonNull(UserHandle.USER_ALL)
+ .putBoolean(UserManager.ENSURE_VERIFY_APPS, true);
}
- // ENSURE_VERIFY_APPS is now enforced globally even if put by profile owner, so move
- // it from local to global bundle for all users who set it.
- UserRestrictionsUtils.moveRestriction(UserManager.ENSURE_VERIFY_APPS,
- mDevicePolicyLocalUserRestrictions, mDevicePolicyGlobalUserRestrictions
- );
}
// DISALLOW_CONFIG_WIFI was made a default guest restriction some time during version 6.
final List<UserInfo> guestUsers = getGuestUsers();
@@ -4135,17 +4009,6 @@
}
@GuardedBy({"mPackagesLock"})
- private void writeAllTargetUsersLP(int originatingUserId) {
- for (int i = 0; i < mDevicePolicyLocalUserRestrictions.size(); i++) {
- int targetUserId = mDevicePolicyLocalUserRestrictions.keyAt(i);
- RestrictionsSet restrictionsSet = mDevicePolicyLocalUserRestrictions.valueAt(i);
- if (restrictionsSet.containsKey(originatingUserId)) {
- writeUserLP(getUserDataNoChecks(targetUserId));
- }
- }
- }
-
- @GuardedBy({"mPackagesLock"})
private void writeUserLP(UserData userData) {
if (DBG) {
debug("writeUserLP " + userData);
@@ -4231,11 +4094,14 @@
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.writeRestrictions(serializer,
mBaseUserRestrictions.getRestrictions(userInfo.id), TAG_RESTRICTIONS);
- getDevicePolicyLocalRestrictionsForTargetUserLR(userInfo.id).writeRestrictions(
- serializer, TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
+
UserRestrictionsUtils.writeRestrictions(serializer,
- mDevicePolicyGlobalUserRestrictions.getRestrictions(userInfo.id),
- TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+ mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
+ TAG_DEVICE_POLICY_RESTRICTIONS);
+
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyUserRestrictions.getRestrictions(userInfo.id),
+ TAG_DEVICE_POLICY_RESTRICTIONS);
}
if (userData.account != null) {
@@ -4303,11 +4169,6 @@
.writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
}
serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
- serializer.startTag(null, TAG_DEVICE_OWNER_USER_ID);
- synchronized (mRestrictionsLock) {
- serializer.attributeInt(null, ATTR_ID, mDeviceOwnerUserId);
- }
- serializer.endTag(null, TAG_DEVICE_OWNER_USER_ID);
int[] userIdsToWrite;
synchronized (mUsersLock) {
userIdsToWrite = new int[mUsers.size()];
@@ -4379,7 +4240,7 @@
UserProperties userProperties = null;
Bundle baseRestrictions = null;
Bundle legacyLocalRestrictions = null;
- RestrictionsSet localRestrictions = null;
+ Bundle localRestrictions = null;
Bundle globalRestrictions = null;
boolean ignorePrepareStorageErrors = true; // default is true for old users
@@ -4445,8 +4306,7 @@
} else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
legacyLocalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS.equals(tag)) {
- localRestrictions = RestrictionsSet.readRestrictions(parser,
- TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
+ localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_ACCOUNT.equals(tag)) {
@@ -4516,19 +4376,15 @@
mBaseUserRestrictions.updateRestrictions(id, baseRestrictions);
}
if (localRestrictions != null) {
- mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
+ mDevicePolicyUserRestrictions.updateRestrictions(id, localRestrictions);
if (legacyLocalRestrictions != null) {
Slog.wtf(LOG_TAG, "Seeing both legacy and current local restrictions in xml");
}
} else if (legacyLocalRestrictions != null) {
- RestrictionsSet legacyLocalRestrictionsSet =
- legacyLocalRestrictions.isEmpty()
- ? new RestrictionsSet()
- : new RestrictionsSet(id, legacyLocalRestrictions);
- mDevicePolicyLocalUserRestrictions.put(id, legacyLocalRestrictionsSet);
+ mDevicePolicyUserRestrictions.updateRestrictions(id, legacyLocalRestrictions);
}
if (globalRestrictions != null) {
- mDevicePolicyGlobalUserRestrictions.updateRestrictions(id,
+ mDevicePolicyUserRestrictions.updateRestrictions(UserHandle.USER_ALL,
globalRestrictions);
}
}
@@ -5364,18 +5220,12 @@
}
} else if (atomTag == FrameworkStatsLog.MULTI_USER_INFO) {
if (UserManager.getMaxSupportedUsers() > 1) {
- int deviceOwnerUserId = UserHandle.USER_NULL;
-
- synchronized (mRestrictionsLock) {
- deviceOwnerUserId = mDeviceOwnerUserId;
- }
-
data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.MULTI_USER_INFO,
UserManager.getMaxSupportedUsers(),
- isUserSwitcherEnabled(deviceOwnerUserId),
+ isUserSwitcherEnabled(UserHandle.USER_ALL),
UserManager.supportsMultipleUsers()
&& !hasUserRestriction(UserManager.DISALLOW_ADD_USER,
- deviceOwnerUserId)));
+ UserHandle.USER_ALL)));
}
} else {
Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
@@ -5843,17 +5693,8 @@
mBaseUserRestrictions.remove(userId);
mAppliedUserRestrictions.remove(userId);
mCachedEffectiveUserRestrictions.remove(userId);
- // Remove local restrictions affecting user
- mDevicePolicyLocalUserRestrictions.delete(userId);
- // Remove local restrictions set by user
- boolean changed = false;
- for (int i = 0; i < mDevicePolicyLocalUserRestrictions.size(); i++) {
- int targetUserId = mDevicePolicyLocalUserRestrictions.keyAt(i);
- changed |= getDevicePolicyLocalRestrictionsForTargetUserLR(targetUserId)
- .remove(userId);
- }
- changed |= mDevicePolicyGlobalUserRestrictions.remove(userId);
- if (changed) {
+ // Remove restrictions affecting the user
+ if (mDevicePolicyUserRestrictions.remove(userId)) {
applyUserRestrictionsForAllUsersLR();
}
}
@@ -6587,10 +6428,12 @@
pw.println();
pw.println("Device properties:");
+ pw.println(" Device policy global restrictions:");
synchronized (mRestrictionsLock) {
- pw.println(" Device owner id:" + mDeviceOwnerUserId);
+ UserRestrictionsUtils.dumpRestrictions(
+ pw, " ",
+ mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL));
}
- pw.println();
pw.println(" Guest restrictions:");
synchronized (mGuestRestrictions) {
UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions);
@@ -6768,13 +6611,10 @@
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.dumpRestrictions(
pw, " ", mBaseUserRestrictions.getRestrictions(userInfo.id));
- pw.println(" Device policy global restrictions:");
+ pw.println(" Device policy restrictions:");
UserRestrictionsUtils.dumpRestrictions(
pw, " ",
- mDevicePolicyGlobalUserRestrictions.getRestrictions(userInfo.id));
- pw.println(" Device policy local restrictions:");
- getDevicePolicyLocalRestrictionsForTargetUserLR(
- userInfo.id).dumpRestrictions(pw, " ");
+ mDevicePolicyUserRestrictions.getRestrictions(userInfo.id));
pw.println(" Effective restrictions:");
UserRestrictionsUtils.dumpRestrictions(
pw, " ",
@@ -6857,6 +6697,11 @@
}
@Override
+ public void setUserRestriction(int userId, @NonNull String key, boolean value) {
+ UserManagerService.this.setUserRestrictionInner(userId, key, value);
+ }
+
+ @Override
public boolean getUserRestriction(@UserIdInt int userId, String key) {
return getUserRestrictions(userId).getBoolean(key);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index e16b0f7..c42a457 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -71,6 +71,10 @@
import java.util.stream.Collectors;
class ActiveAdmin {
+
+ private final int userId;
+ public final boolean isPermissionBased;
+
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
@@ -356,8 +360,20 @@
String mSmsPackage;
ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
+ this.userId = -1;
this.info = info;
this.isParent = isParent;
+ this.isPermissionBased = false;
+ }
+
+ ActiveAdmin(int userId, boolean permissionBased) {
+ if (permissionBased == false) {
+ throw new IllegalArgumentException("Can only pass true for permissionBased admin");
+ }
+ this.userId = userId;
+ this.isPermissionBased = permissionBased;
+ this.isParent = false;
+ this.info = null;
}
ActiveAdmin getParentActiveAdmin() {
@@ -374,10 +390,16 @@
}
int getUid() {
+ if (isPermissionBased) {
+ return -1;
+ }
return info.getActivityInfo().applicationInfo.uid;
}
public UserHandle getUserHandle() {
+ if (isPermissionBased) {
+ return UserHandle.of(userId);
+ }
return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid));
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index a5b9d43..6d51bd7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -133,9 +133,9 @@
// Create or get the permission-based admin. The permission-based admin will not have a
// DeviceAdminInfo or ComponentName.
- ActiveAdmin createOrGetPermissionBasedAdmin() {
+ ActiveAdmin createOrGetPermissionBasedAdmin(int userId) {
if (mPermissionBasedAdmin == null) {
- mPermissionBasedAdmin = new ActiveAdmin(/* info= */ null, /* parent= */ false);
+ mPermissionBasedAdmin = new ActiveAdmin(userId, /* permissionBased= */ true);
}
return mPermissionBasedAdmin;
}
@@ -509,7 +509,7 @@
Slogf.w(TAG, e, "Failed loading admin %s", name);
}
} else if ("permission-based-admin".equals(tag)) {
- ActiveAdmin ap = new ActiveAdmin(/* info= */ null, /* parent= */ false);
+ ActiveAdmin ap = new ActiveAdmin(policy.mUserId, /* permissionBased= */ true);
ap.readFromXml(parser, /* overwritePolicies= */ false);
policy.mPermissionBasedAdmin = ap;
} else if ("delegation".equals(tag)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 821a5f6..5e71a55 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3616,7 +3616,7 @@
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+ if ((admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD))
&& admin.passwordExpirationTimeout > 0L
&& now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
&& admin.passwordExpirationDate > 0L) {
@@ -4296,15 +4296,26 @@
}
}
+ @GuardedBy("getLockObject()")
private List<ActiveAdmin> getActiveAdminsForLockscreenPoliciesLocked(int userHandle) {
if (isSeparateProfileChallengeEnabled(userHandle)) {
+
+ if (isPermissionCheckFlagEnabled()) {
+ return getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userHandle);
+ }
// If this user has a separate challenge, only return its restrictions.
return getUserDataUnchecked(userHandle).mAdminList;
}
// If isSeparateProfileChallengeEnabled is false and userHandle points to a managed profile
// we need to query the parent user who owns the credential.
- return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
- (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+ if (isPermissionCheckFlagEnabled()) {
+ return getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(getProfileParentId(userHandle),
+ (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+ } else {
+ return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
+ (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+ }
+
}
/**
@@ -4340,7 +4351,14 @@
@GuardedBy("getLockObject()")
private List<ActiveAdmin> getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
int userHandle) {
- List<ActiveAdmin> list = getActiveAdminsForAffectedUserLocked(userHandle);
+ List<ActiveAdmin> list;
+
+ if (isManagedProfile(userHandle)) {
+ list = getUserDataUnchecked(userHandle).mAdminList;
+ }
+ list = getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(userHandle,
+ /* shouldIncludeProfileAdmins */ (user) -> false);
+
if (getUserData(userHandle).mPermissionBasedAdmin != null) {
list.add(getUserData(userHandle).mPermissionBasedAdmin);
}
@@ -4378,6 +4396,44 @@
return admins;
}
+ /**
+ * Returns the list of admins on the given user, as well as parent admins for each managed
+ * profile associated with the given user. Optionally also include the admin of each managed
+ * profile.
+ * <p> Should not be called on a profile user.
+ */
+ @GuardedBy("getLockObject()")
+ private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(int userHandle,
+ Predicate<UserInfo> shouldIncludeProfileAdmins) {
+ ArrayList<ActiveAdmin> admins = new ArrayList<>();
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+ if (userInfo.id == userHandle) {
+ admins.addAll(policy.mAdminList);
+ if (policy.mPermissionBasedAdmin != null) {
+ admins.add(policy.mPermissionBasedAdmin);
+ }
+ } else if (userInfo.isManagedProfile()) {
+ for (int i = 0; i < policy.mAdminList.size(); i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.hasParentActiveAdmin()) {
+ admins.add(admin.getParentActiveAdmin());
+ }
+ if (shouldIncludeProfileAdmins.test(userInfo)) {
+ admins.add(admin);
+ }
+ }
+ if (policy.mPermissionBasedAdmin != null
+ && shouldIncludeProfileAdmins.test(userInfo)) {
+ admins.add(policy.mPermissionBasedAdmin);
+ }
+ }
+ }
+ });
+ return admins;
+ }
+
private boolean isSeparateProfileChallengeEnabled(int userHandle) {
return mInjector.binderWithCleanCallingIdentity(() ->
mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle));
@@ -4697,6 +4753,7 @@
* Return a single admin's expiration date/time, or the min (soonest) for all admins.
* Returns 0 if not configured.
*/
+ @GuardedBy("getLockObject()")
private long getPasswordExpirationLocked(ComponentName who, int userHandle, boolean parent) {
long timeout = 0L;
@@ -5282,11 +5339,12 @@
adminPackageName, userId, affectedUserId, complexity);
}
}
-
+ @GuardedBy("getLockObject()")
private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle) {
return getAggregatedPasswordComplexityLocked(userHandle, false);
}
+ @GuardedBy("getLockObject()")
private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle,
boolean deviceWideOnly) {
ensureLocked();
@@ -5469,6 +5527,7 @@
* profile.
* Returns {@code null} if no participating admin has that policy set.
*/
+ @GuardedBy("getLockObject()")
private ActiveAdmin getAdminWithMinimumFailedPasswordsForWipeLocked(
int userHandle, boolean parent) {
int count = 0;
@@ -5699,6 +5758,7 @@
}
}
+ @GuardedBy("getLockObject()")
private void updateMaximumTimeToLockLocked(@UserIdInt int userId) {
// Update the profile's timeout
if (isManagedProfile(userId)) {
@@ -5727,6 +5787,7 @@
});
}
+ @GuardedBy("getLockObject()")
private void updateProfileLockTimeoutLocked(@UserIdInt int userId) {
final long timeMs;
if (isSeparateProfileChallengeEnabled(userId)) {
@@ -7459,9 +7520,15 @@
final String adminName;
final ComponentName adminComp;
if (admin != null) {
- adminComp = admin.info.getComponent();
- adminName = adminComp.flattenToShortString();
- event.setAdmin(adminComp);
+ if (admin.isPermissionBased) {
+ adminComp = null;
+ adminName = caller.getPackageName();
+ event.setAdmin(adminName);
+ } else {
+ adminComp = admin.info.getComponent();
+ adminName = adminComp.flattenToShortString();
+ event.setAdmin(adminComp);
+ }
} else {
adminComp = null;
adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
@@ -7750,13 +7817,7 @@
|| hasCallingPermission(permission.MASTER_CLEAR)
|| hasCallingPermission(MANAGE_DEVICE_POLICY_FACTORY_RESET),
"Must be called by the FRP management agent on device");
- // TODO(b/261999445): Remove
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.getUserId(frpManagementAgentUid));
- }
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
@@ -7927,12 +7988,13 @@
*
* @return the set of user IDs that have been affected
*/
+ @GuardedBy("getLockObject()")
private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
final ArraySet<Integer> affectedUserIds = new ArraySet<>();
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
for (int i = 0; i < admins.size(); i++) {
ActiveAdmin admin = admins.get(i);
- if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+ if (admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
affectedUserIds.add(admin.getUserHandle().getIdentifier());
long timeout = admin.passwordExpirationTimeout;
admin.passwordExpirationDate =
@@ -8026,6 +8088,9 @@
*/
private int getUserIdToWipeForFailedPasswords(ActiveAdmin admin) {
final int userId = admin.getUserHandle().getIdentifier();
+ if (admin.isPermissionBased) {
+ return userId;
+ }
final ComponentName component = admin.info.getComponent();
return isProfileOwnerOfOrganizationOwnedDevice(component, userId)
? getProfileParentId(userId) : userId;
@@ -9653,6 +9718,15 @@
return admin;
}
+ ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked() {
+ ensureLocked();
+ ActiveAdmin doOrPo = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+ if (isPermissionCheckFlagEnabled() && doOrPo == null) {
+ return getUserData(0).mPermissionBasedAdmin;
+ }
+ return doOrPo;
+ }
+
ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(int userId) {
ensureLocked();
ActiveAdmin admin = getDeviceOwnerAdminLocked();
@@ -10659,9 +10733,12 @@
return false;
}
- final ComponentName profileOwner = getProfileOwnerAsUser(userId);
- if (profileOwner == null) {
- return false;
+ if (!isPermissionCheckFlagEnabled()) {
+ // TODO: Figure out if something like this needs to be restored for policy engine
+ final ComponentName profileOwner = getProfileOwnerAsUser(userId);
+ if (profileOwner == null) {
+ return false;
+ }
}
// Managed profiles are not allowed to use lock task
@@ -11884,7 +11961,7 @@
synchronized (getLockObject()) {
List<String> result = null;
// Only device or profile owners can have permitted lists set.
- List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(userId);
+ List<ActiveAdmin> admins = getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userId);
for (ActiveAdmin admin: admins) {
List<String> fromAdmin = admin.permittedInputMethods;
if (fromAdmin != null) {
@@ -12597,7 +12674,6 @@
}
return policies.get(enforcingAdmin).getValue();
} else {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller,
@@ -13623,7 +13699,6 @@
new BooleanPolicyValue(uninstallBlocked),
caller.getUserId());
} else {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
|| isFinancedDeviceOwner(caller)))
@@ -14265,7 +14340,7 @@
final int userId = mInjector.userHandleGetCallingUserId();
// Is it ok to just check that no active policies exist currently?
- if (mDevicePolicyEngine.hasActivePolicies()) {
+ if (isDevicePolicyEngineFlagEnabled() && mDevicePolicyEngine.hasActivePolicies()) {
LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.LOCK_TASK, userId);
if (policy == null) {
@@ -15814,7 +15889,7 @@
if (admin.mPasswordPolicy.quality < minPasswordQuality) {
return false;
}
- return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ return admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
}
@Override
@@ -21451,13 +21526,7 @@
}
synchronized (getLockObject()) {
ActiveAdmin admin;
- // TODO(b/261999445): remove
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
return admin != null ? admin.mWifiSsidPolicy : null;
}
}
@@ -22454,7 +22523,7 @@
return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
}
if (admin == null) {
- admin = getUserData(userId).createOrGetPermissionBasedAdmin();
+ admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
}
return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
}
@@ -23014,26 +23083,12 @@
return admins;
}
- // TODO: This can actually accept an EnforcingAdmin that gets created in the permission check
- // method.
private boolean useDevicePolicyEngine(CallerIdentity caller, @Nullable String delegateScope) {
- if (!isCallerActiveAdminOrDelegate(caller, delegateScope)) {
- if (!isDevicePolicyEngineFlagEnabled()) {
- throw new IllegalStateException("Non DPC caller can't set device policies.");
- }
- if (hasDPCsNotSupportingCoexistence()) {
- throw new IllegalStateException("Non DPC caller can't set device policies with "
- + "existing legacy admins on the device.");
- }
- return true;
- } else {
- return isDevicePolicyEngineEnabled();
- }
+ return isDevicePolicyEngineEnabled();
}
private boolean isDevicePolicyEngineEnabled() {
- return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence()
- && isPermissionCheckFlagEnabled();
+ return isDevicePolicyEngineFlagEnabled() && isPermissionCheckFlagEnabled();
}
private boolean isDevicePolicyEngineFlagEnabled() {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/RestrictionsSetTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/RestrictionsSetTest.java
index e7adf7b..8345a43 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/RestrictionsSetTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/RestrictionsSetTest.java
@@ -32,6 +32,7 @@
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -138,6 +139,7 @@
}
@Test
+ @Ignore("b/268334580")
public void testGetEnforcingUsers_hasEnforcingUser() {
mRestrictionsSet.updateRestrictions(originatingUserId,
newRestrictions(UserManager.ENSURE_VERIFY_APPS));
@@ -154,6 +156,7 @@
}
@Test
+ @Ignore("b/268334580")
public void testGetEnforcingUsers_hasMultipleEnforcingUsers() {
int originatingUserId2 = 10;
mRestrictionsSet.updateRestrictions(originatingUserId,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index d999aa3..2273fcd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -230,7 +230,7 @@
mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null));
mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null));
- mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1, userTypeVersion);
+ mUserManagerService.upgradeIfNecessaryLP(versionToTest - 1, userTypeVersion);
assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED));
assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d4374a9..fd54293 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1049,7 +1049,8 @@
@Override
public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
- @Nullable String resolvedType, @Nullable String attributionTag) {
+ @Nullable String resolvedType, @NonNull String attributionTag,
+ @NonNull Bundle bundle) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1060,7 +1061,7 @@
final long caller = Binder.clearCallingIdentity();
try {
return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
- callingUid, token, intent, resolvedType);
+ callingUid, token, intent, resolvedType, bundle);
} finally {
Binder.restoreCallingIdentity(caller);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ad0e921..96b69f8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -29,7 +29,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
import android.app.ApplicationExitInfo;
@@ -376,7 +375,8 @@
@GuardedBy("this")
public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
- int callingUid, IBinder token, Intent intent, String resolvedType) {
+ int callingUid, IBinder token, Intent intent, String resolvedType,
+ @NonNull Bundle bundle) {
try {
if (mActiveSession == null || token != mActiveSession.mToken) {
Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -388,10 +388,10 @@
}
intent = new Intent(intent);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+ // TODO: make the key public hidden
+ bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT);
return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
- callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
+ callingPid, callingUid, intent, resolvedType, bundle, mUser);
} catch (RemoteException e) {
throw new IllegalStateException("Unexpected remote error", e);
}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
index d6f8012..abf2b55 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
@@ -16,6 +16,7 @@
package android.telephony.satellite;
+import android.telephony.satellite.ISatelliteDatagramReceiverAck;
import android.telephony.satellite.SatelliteDatagram;
/**
@@ -24,8 +25,15 @@
*/
oneway interface ISatelliteDatagramCallback {
/**
- * Called when there are incoming datagrams to be received.
- * @param datagrams Array of datagrams to be received over satellite.
+ * Called when datagrams are received from satellite.
+ *
+ * @param datagramId An id that uniquely identifies incoming datagram.
+ * @param datagram datagram received from satellite.
+ * @param pendingCount Number of datagrams yet to be received from satellite.
+ * @param callback This callback will be used by datagram receiver app to send ack back to
+ * Telephony. If the callback is not received within five minutes,
+ * Telephony will resend the datagrams.
*/
- void onSatelliteDatagrams(in SatelliteDatagram[] datagrams);
+ void onSatelliteDatagramReceived(long datagramId, in SatelliteDatagram datagram,
+ int pendingCount, ISatelliteDatagramReceiverAck callback);
}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramReceiverAck.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramReceiverAck.aidl
new file mode 100644
index 0000000..eeb0ac5
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramReceiverAck.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteDatagram;
+
+/**
+ * Interface for satellite datagram receiver acknowledgement.
+ * @hide
+ */
+oneway interface ISatelliteDatagramReceiverAck {
+ /**
+ * This callback will be used by datagram receiver app to send ack back to
+ * Telephony. If the callback is not received within five minutes,
+ * then Telephony will resend the datagram again.
+ *
+ * @param datagramId An id that uniquely identifies datagram
+ * received by satellite datagram receiver app.
+ * This should match with datagramId passed in
+ * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(
+ * long, SatelliteDatagram, int, ISatelliteDatagramReceiverAck)}.
+ * Upon receiving the ack, Telephony will remove the datagram from
+ * the persistent memory.
+ */
+ void acknowledgeSatelliteDatagramReceived(in long datagramId);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index 484b783..2c3884c 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -38,10 +38,12 @@
}
@Override
- public void onSatelliteDatagrams(SatelliteDatagram[] datagrams) {
+ public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
+ int pendingCount, ISatelliteDatagramReceiverAck callback) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> mLocalCallback.onSatelliteDatagrams(datagrams));
+ mExecutor.execute(() -> mLocalCallback.onSatelliteDatagramReceived(datagramId,
+ datagram, pendingCount, callback));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -54,9 +56,14 @@
/**
* Called when there are incoming datagrams to be received.
- * @param datagrams Datagrams to be received over satellite.
+ * @param datagramId An id that uniquely identifies incoming datagram.
+ * @param datagram datagram to be received over satellite.
+ * @param pendingCount Number of datagrams yet to be received by the app.
+ * @param callback This callback will be used by datagram receiver app to send ack back to
+ * Telephony.
*/
- public void onSatelliteDatagrams(SatelliteDatagram[] datagrams) {
+ public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
+ int pendingCount, ISatelliteDatagramReceiverAck callback) {
// Base Implementation
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 8fbf640..dbc0ed9 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -130,7 +130,7 @@
/**
* Bundle key to get the response from
- * {@link #requestMaxCharactersPerSatelliteTextMessage(Executor, OutcomeReceiver)}.
+ * {@link #requestMaxSizePerSendingDatagram(Executor, OutcomeReceiver)} .
* @hide
*/
public static final String KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT =
@@ -159,6 +159,13 @@
public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
/**
+ * Bundle key to get the respoonse from
+ * {@link #sendSatelliteDatagram(long, int, SatelliteDatagram, Executor, OutcomeReceiver)}.
+ * @hide
+ */
+ public static final String KEY_SEND_SATELLITE_DATAGRAM = "send_satellite_datagram";
+
+ /**
* The request was successfully processed.
*/
public static final int SATELLITE_ERROR_NONE = 0;
@@ -541,8 +548,8 @@
@Retention(RetentionPolicy.SOURCE)
public @interface SatelliteModemState {}
- /** Datagram type indicating that the datagram to be sent or received is of type SOS SMS. */
- public static final int DATAGRAM_TYPE_SOS_SMS = 0;
+ /** Datagram type indicating that the datagram to be sent or received is of type SOS message. */
+ public static final int DATAGRAM_TYPE_SOS_MESSAGE = 0;
/** Datagram type indicating that the datagram to be sent or received is of type
* location sharing. */
@@ -551,7 +558,7 @@
@IntDef(
prefix = "DATAGRAM_TYPE_",
value = {
- DATAGRAM_TYPE_SOS_SMS,
+ DATAGRAM_TYPE_SOS_MESSAGE,
DATAGRAM_TYPE_LOCATION_SHARING,
})
@Retention(RetentionPolicy.SOURCE)
@@ -651,19 +658,20 @@
}
/**
- * Request to get the maximum number of characters per text message on satellite.
+ * Request to get the maximum number of bytes per datagram that can be sent to satellite.
*
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
- * will return the maximum number of characters per text message on satellite.
+ * will return the maximum number of bytes per datagram that can be sent to
+ * satellite.
* If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestMaxCharactersPerSatelliteTextMessage(
+ public void requestMaxSizePerSendingDatagram(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Integer, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -693,7 +701,7 @@
}
}
};
- telephony.requestMaxCharactersPerSatelliteTextMessage(mSubId, receiver);
+ telephony.requestMaxSizePerSendingDatagram(mSubId, receiver);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1039,7 +1047,8 @@
*
* This method requests modem to check if there are any pending datagrams to be received over
* satellite. If there are any incoming datagrams, they will be received via
- * {@link SatelliteDatagramCallback#onSatelliteDatagrams(SatelliteDatagram[])})}.
+ * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
+ * ISatelliteDatagramReceiverAck)}
*
* @param executor The executor on which the result listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
@@ -1076,39 +1085,60 @@
/**
* Send datagram over satellite.
*
- * Gateway encodes SOS SMS or location sharing message into a datagram and passes it as input to
- * this method. Datagram received here will be passed down to modem without any encoding or
- * encryption.
+ * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+ * input to this method. Datagram received here will be passed down to modem without any
+ * encoding or encryption.
*
+ * @param datagramId An id that uniquely identifies datagram requested to be sent.
* @param datagramType datagram type indicating whether the datagram is of type
* SOS_SMS or LOCATION_SHARING.
* @param datagram encoded gateway datagram which is encrypted by the caller.
* Datagram will be passed down to modem without any encoding or encryption.
* @param executor The executor on which the result listener will be called.
- * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+ * @param callback The callback object to which the result will be returned.
+ * If datagram is sent successfully, then
+ * {@link OutcomeReceiver#onResult(Object)} will return datagramId.
+ * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+ * will return a {@link SatelliteException} with the {@link SatelliteError}.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void sendSatelliteDatagram(@DatagramType int datagramType,
+ public void sendSatelliteDatagram(long datagramId, @DatagramType int datagramType,
@NonNull SatelliteDatagram datagram, @NonNull @CallbackExecutor Executor executor,
- @SatelliteError @NonNull Consumer<Integer> resultListener) {
+ @NonNull OutcomeReceiver<Long, SatelliteException> callback) {
Objects.requireNonNull(datagram);
Objects.requireNonNull(executor);
- Objects.requireNonNull(resultListener);
+ Objects.requireNonNull(callback);
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ ResultReceiver receiver = new ResultReceiver(null) {
@Override
- public void accept(int result) {
- executor.execute(() -> Binder.withCleanCallingIdentity(
- () -> resultListener.accept(result)));
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SEND_SATELLITE_DATAGRAM)) {
+ long resultDatagramId = resultData
+ .getLong(KEY_SEND_SATELLITE_DATAGRAM);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(resultDatagramId)));
+ } else {
+ loge("KEY_SEND_SATELLITE_DATAGRAM does not exist.");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(
+ new SatelliteException(SATELLITE_REQUEST_FAILED))));
+ }
+
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
}
};
- telephony.sendSatelliteDatagram(mSubId, datagramType, datagram, internalCallback);
+ telephony.sendSatelliteDatagram(mSubId, datagramId, datagramType, datagram,
+ receiver);
} else {
throw new IllegalStateException("telephony service is null.");
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 62e087f..5bf55ef 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2772,15 +2772,15 @@
in ISatellitePositionUpdateCallback callback);
/**
- * Request to get the maximum number of characters per text message on satellite.
+ * Request to get the maximum number of bytes per datagram that can be sent to satellite.
*
* @param subId The subId of the subscription to get the maximum number of characters for.
* @param receiver Result receiver to get the error code of the request and the requested
- * maximum number of characters per text message on satellite.
+ * maximum number of bytes per datagram that can be sent to satellite.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestMaxCharactersPerSatelliteTextMessage(int subId, in ResultReceiver receiver);
+ void requestMaxSizePerSendingDatagram(int subId, in ResultReceiver receiver);
/**
* Register the subscription with a satellite provider.
@@ -2912,14 +2912,16 @@
* Send datagram over satellite.
*
* @param subId The subId of the subscription to send satellite datagrams for.
+ * @param datagramId An id that uniquely identifies datagram requested to be sent.
* @param datagramType Type of datagram.
* @param datagram Datagram to send over satellite.
- * @param callback The callback to get the error code of the request.
+ * @param receiver Result receiver to get the datagramId if datagram is sent successfully else
+ * error code of the request.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void sendSatelliteDatagram(int subId, int datagramType, in SatelliteDatagram datagram,
- IIntegerConsumer callback);
+ void sendSatelliteDatagram(int subId, long datagramId, int datagramType,
+ in SatelliteDatagram datagram, in ResultReceiver receiver);
/**
* Request to get whether satellite communication is allowed for the current location.
diff --git a/tools/processors/immutability/TEST_MAPPING b/tools/processors/immutability/TEST_MAPPING
deleted file mode 100644
index 4e8e238..0000000
--- a/tools/processors/immutability/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "ImmutabilityAnnotationProcessorUnitTests"
- }
- ]
-}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index 13084f4..f3af062 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -59,7 +59,7 @@
private static final String TAG = SharedConnectivityService.class.getSimpleName();
private static final boolean DEBUG = true;
- private final Handler mHandler;
+ private Handler mHandler;
private final List<ISharedConnectivityCallback> mCallbacks = new ArrayList<>();
// Used to find DeathRecipient when unregistering a callback to call unlinkToDeath.
private final Map<ISharedConnectivityCallback, DeathRecipient> mDeathRecipientMap =
@@ -71,14 +71,6 @@
private TetherNetworkConnectionStatus mTetherNetworkConnectionStatus;
private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus;
- public SharedConnectivityService() {
- mHandler = new Handler(getMainLooper());
- }
-
- public SharedConnectivityService(@NonNull Handler handler) {
- mHandler = handler;
- }
-
private final class DeathRecipient implements IBinder.DeathRecipient {
ISharedConnectivityCallback mCallback;
@@ -97,6 +89,7 @@
@Nullable
public final IBinder onBind(@NonNull Intent intent) {
if (DEBUG) Log.i(TAG, "onBind intent=" + intent);
+ mHandler = new Handler(getMainLooper());
return new ISharedConnectivityService.Stub() {
@Override
public void registerCallback(ISharedConnectivityCallback callback) {
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index fb8d7bf..d7f7fea 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -17,16 +17,21 @@
package android.net.wifi.sharedconnectivity.service;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.when;
+import android.content.Context;
import android.content.Intent;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
import android.net.wifi.sharedconnectivity.app.TetherNetwork;
-import android.os.Handler;
-import android.os.test.TestLooper;
+import android.os.Looper;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Unit tests for {@link android.net.wifi.sharedconnectivity.service.SharedConnectivityService}.
@@ -34,6 +39,33 @@
@SmallTest
public class SharedConnectivityServiceTest {
+ @Mock
+ Context mContext;
+
+ static class FakeSharedConnectivityService extends SharedConnectivityService {
+ public void attachBaseContext(Context context) {
+ super.attachBaseContext(context);
+ }
+
+ @Override
+ public void onConnectTetherNetwork(@NonNull TetherNetwork network) {}
+
+ @Override
+ public void onDisconnectTetherNetwork(@NonNull TetherNetwork network) {}
+
+ @Override
+ public void onConnectKnownNetwork(@NonNull KnownNetwork network) {}
+
+ @Override
+ public void onForgetKnownNetwork(@NonNull KnownNetwork network) {}
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ }
+
/**
* Verifies service returns
*/
@@ -51,18 +83,8 @@
}
private SharedConnectivityService createService() {
- return new SharedConnectivityService(new Handler(new TestLooper().getLooper())) {
- @Override
- public void onConnectTetherNetwork(TetherNetwork network) {}
-
- @Override
- public void onDisconnectTetherNetwork(TetherNetwork network) {}
-
- @Override
- public void onConnectKnownNetwork(KnownNetwork network) {}
-
- @Override
- public void onForgetKnownNetwork(KnownNetwork network) {}
- };
+ FakeSharedConnectivityService service = new FakeSharedConnectivityService();
+ service.attachBaseContext(mContext);
+ return service;
}
}