Merge TQ1A.230105.002 to aosp-master - DO NOT MERGE

Merged-In: I6c001e8ab8ef233c4e764d7c6753429b472dac94
Merged-In: If3c3110031ae04df59ed4e488714d187638d801a

Change-Id: I200e0524debf868b3764affa35030a7831e7ecbe
This commit is contained in:
Bill Yi
2023-01-06 07:05:49 -08:00
24 changed files with 673 additions and 247 deletions

View File

@@ -107,6 +107,34 @@ public class UsbDeviceConnection {
}
}
/**
* This is meant to be called by UsbRequest's queue() in order to synchronize on
* UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
*/
/* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) {
synchronized (mLock) {
if (!isOpen()) {
return false;
}
return request.queueIfConnectionOpen(buffer, length);
}
}
/**
* This is meant to be called by UsbRequest's queue() in order to synchronize on
* UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
*/
/* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) {
synchronized (mLock) {
if (!isOpen()) {
return false;
}
return request.queueIfConnectionOpen(buffer);
}
}
/**
* Releases all system resources related to the device.
* Once the object is closed it cannot be used again.

View File

@@ -113,11 +113,13 @@ public class UsbRequest {
* Releases all resources related to this request.
*/
public void close() {
if (mNativeContext != 0) {
mEndpoint = null;
mConnection = null;
native_close();
mCloseGuard.close();
synchronized (mLock) {
if (mNativeContext != 0) {
mEndpoint = null;
mConnection = null;
native_close();
mCloseGuard.close();
}
}
}
@@ -191,10 +193,32 @@ public class UsbRequest {
*/
@Deprecated
public boolean queue(ByteBuffer buffer, int length) {
UsbDeviceConnection connection = mConnection;
if (connection == null) {
// The expected exception by CTS Verifier - USB Device test
throw new NullPointerException("invalid connection");
}
// Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
// the connection being closed while queueing.
return connection.queueRequest(this, buffer, length);
}
/**
* This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
* there, to prevent the connection being closed while queueing.
*/
/* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) {
UsbDeviceConnection connection = mConnection;
if (connection == null || !connection.isOpen()) {
// The expected exception by CTS Verifier - USB Device test
throw new NullPointerException("invalid connection");
}
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
&& length > MAX_USBFS_BUFFER_SIZE) {
length = MAX_USBFS_BUFFER_SIZE;
}
@@ -243,6 +267,28 @@ public class UsbRequest {
* @return true if the queueing operation succeeded
*/
public boolean queue(@Nullable ByteBuffer buffer) {
UsbDeviceConnection connection = mConnection;
if (connection == null) {
// The expected exception by CTS Verifier - USB Device test
throw new IllegalStateException("invalid connection");
}
// Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
// the connection being closed while queueing.
return connection.queueRequest(this, buffer);
}
/**
* This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
* there, to prevent the connection being closed while queueing.
*/
/* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) {
UsbDeviceConnection connection = mConnection;
if (connection == null || !connection.isOpen()) {
// The expected exception by CTS Verifier - USB Device test
throw new IllegalStateException("invalid connection");
}
// Request need to be initialized
Preconditions.checkState(mNativeContext != 0, "request is not initialized");
@@ -260,7 +306,7 @@ public class UsbRequest {
mIsUsingNewQueue = true;
wasQueued = native_queue(null, 0, 0);
} else {
if (mConnection.getContext().getApplicationInfo().targetSdkVersion
if (connection.getContext().getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.P) {
// Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
@@ -363,11 +409,12 @@ public class UsbRequest {
* @return true if cancelling succeeded
*/
public boolean cancel() {
if (mConnection == null) {
UsbDeviceConnection connection = mConnection;
if (connection == null) {
return false;
}
return mConnection.cancelRequest(this);
return connection.cancelRequest(this);
}
/**
@@ -382,7 +429,8 @@ public class UsbRequest {
* @return true if cancelling succeeded.
*/
/* package */ boolean cancelIfOpen() {
if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) {
UsbDeviceConnection connection = mConnection;
if (mNativeContext == 0 || (connection != null && !connection.isOpen())) {
Log.w(TAG,
"Detected attempt to cancel a request on a connection which isn't open");
return false;

View File

@@ -3344,9 +3344,26 @@ public final class Settings {
}
}
// Fetch all flags for the namespace at once for caching purposes
Bundle b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
Bundle b;
// b/252663068: if we're in system server and the caller did not call
// clearCallingIdentity, the read would fail due to mismatched AttributionSources.
// TODO(b/256013480): remove this bypass after fixing the callers in system server.
if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
&& Settings.isInSystemServer()
&& Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
// Fetch all flags for the namespace at once for caching purposes
b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
// Fetch all flags for the namespace at once for caching purposes
b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
}
if (b == null) {
// Invalid response, return an empty map
return keyValues;

View File

@@ -172,14 +172,14 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
@Override
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
return true;
}
@Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
activity.startActivityAsUser(mResolvedIntent, options, user);
return false;
}
@@ -224,13 +224,6 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
}
};
private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
final int currentUserId = UserHandle.myUserId();
if (targetUserId != currentUserId) {
intent.fixUris(currentUserId);
}
}
private DisplayResolveInfo(Parcel in) {
mDisplayLabel = in.readCharSequence();
mExtendedInfo = in.readCharSequence();

View File

@@ -232,6 +232,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
}
intent.setComponent(mChooserTarget.getComponentName());
intent.putExtras(mChooserTarget.getIntentExtras());
TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId);
// Important: we will ignore the target security checks in ActivityManager
// if and only if the ChooserTarget's target package is the same package

View File

@@ -130,4 +130,15 @@ public interface TargetInfo {
* @return true if this target should be pinned to the front by the request of the user
*/
boolean isPinned();
/**
* Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called
* before launching the intent as another user.
*/
static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
final int currentUserId = UserHandle.myUserId();
if (targetUserId != currentUserId) {
intent.fixUris(currentUserId);
}
}
}

View File

@@ -48,6 +48,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -376,9 +377,11 @@ final class SettingsState {
Setting newSetting = new Setting(name, oldSetting.getValue(), null,
oldSetting.getPackageName(), oldSetting.getTag(), false,
oldSetting.getId());
int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0,
oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
mSettings.put(name, newSetting);
updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
scheduleWriteIfNeededLocked();
}
}
@@ -410,6 +413,13 @@ final class SettingsState {
Setting oldState = mSettings.get(name);
String oldValue = (oldState != null) ? oldState.value : null;
String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
String newDefaultValue = makeDefault ? value : oldDefaultValue;
int newSize = getNewMemoryUsagePerPackageLocked(packageName,
oldValue == null ? name.length() : 0 /* deltaKeySize */,
oldValue, value, oldDefaultValue, newDefaultValue);
checkNewMemoryUsagePerPackageLocked(packageName, newSize);
Setting newState;
if (oldState != null) {
@@ -430,8 +440,7 @@ final class SettingsState {
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
oldDefaultValue, newState.getDefaultValue());
updateMemoryUsagePerPackageLocked(packageName, newSize);
scheduleWriteIfNeededLocked();
@@ -552,13 +561,18 @@ final class SettingsState {
}
Setting oldState = mSettings.remove(name);
if (oldState == null) {
return false;
}
int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName,
-name.length() /* deltaKeySize */,
oldState.value, null, oldState.defaultValue, null);
FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
/* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
null, oldState.defaultValue, null);
updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
@@ -575,20 +589,23 @@ final class SettingsState {
}
Setting setting = mSettings.get(name);
if (setting == null) {
return false;
}
Setting oldSetting = new Setting(setting);
String oldValue = setting.getValue();
String oldDefaultValue = setting.getDefaultValue();
int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue,
oldDefaultValue, oldDefaultValue, oldDefaultValue);
checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
if (!setting.reset()) {
return false;
}
String newValue = setting.getValue();
String newDefaultValue = setting.getDefaultValue();
updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
newValue, oldDefaultValue, newDefaultValue);
updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
@@ -696,38 +713,49 @@ final class SettingsState {
}
@GuardedBy("mLock")
private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
String newValue, String oldDefaultValue, String newDefaultValue) {
if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
private boolean isExemptFromMemoryUsageCap(String packageName) {
return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
|| SYSTEM_PACKAGE_NAME.equals(packageName);
}
@GuardedBy("mLock")
private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
throws IllegalStateException {
if (isExemptFromMemoryUsageCap(packageName)) {
return;
}
if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
return;
}
final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
final int newValueSize = (newValue != null) ? newValue.length() : 0;
final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
final int deltaSize = newValueSize + newDefaultValueSize
- oldValueSize - oldDefaultValueSize;
Integer currentSize = mPackageToMemoryUsage.get(packageName);
final int newSize = Math.max((currentSize != null)
? currentSize + deltaSize : deltaSize, 0);
if (newSize > mMaxBytesPerAppPackage) {
throw new IllegalStateException("You are adding too many system settings. "
+ "You should stop using system settings for app specific data"
+ " package: " + packageName);
}
}
@GuardedBy("mLock")
private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize,
String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) {
if (isExemptFromMemoryUsageCap(packageName)) {
return 0;
}
final Integer currentSize = mPackageToMemoryUsage.get(packageName);
final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
final int newValueSize = (newValue != null) ? newValue.length() : 0;
final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize
- oldValueSize - oldDefaultValueSize;
return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
}
@GuardedBy("mLock")
private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
if (isExemptFromMemoryUsageCap(packageName)) {
return;
}
if (DEBUG) {
Slog.i(LOG_TAG, "Settings for package: " + packageName
+ " size: " + newSize + " bytes.");
}
mPackageToMemoryUsage.put(packageName, newSize);
}
@@ -1556,4 +1584,11 @@ final class SettingsState {
}
return false;
}
@VisibleForTesting
public int getMemoryUsage(String packageName) {
synchronized (mLock) {
return mPackageToMemoryUsage.getOrDefault(packageName, 0);
}
}
}

View File

@@ -20,6 +20,8 @@ import android.test.AndroidTestCase;
import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.google.common.base.Strings;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -276,4 +278,132 @@ public class SettingsStateTest extends AndroidTestCase {
settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
return settingsState;
}
public void testInsertSetting_memoryUsage() {
SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
// No exception should be thrown when there is no cap
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
null, false, "p1");
settingsState.deleteSettingLocked(SETTING_NAME);
settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
// System package doesn't have memory usage limit
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
null, false, SYSTEM_PACKAGE);
settingsState.deleteSettingLocked(SETTING_NAME);
// Should not throw if usage is under the cap
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975),
null, false, "p1");
settingsState.deleteSettingLocked(SETTING_NAME);
try {
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
null, false, "p1");
fail("Should throw because it exceeded per package memory usage");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("p1"));
}
try {
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
null, false, "p1");
fail("Should throw because it exceeded per package memory usage");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("p1"));
}
assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
try {
settingsState.insertSettingLocked(Strings.repeat("A", 20001), "",
null, false, "p1");
fail("Should throw because it exceeded per package memory usage");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("You are adding too many system settings"));
}
}
public void testMemoryUsagePerPackage() {
SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
// Test inserting one key with default
final String testKey1 = SETTING_NAME;
final String testValue1 = Strings.repeat("A", 100);
settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE);
int expectedMemUsage = testKey1.length() + testValue1.length()
+ testValue1.length() /* size for default */;
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test inserting another key
final String testKey2 = SETTING_NAME + "2";
settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE);
expectedMemUsage += testKey2.length() + testValue1.length();
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test updating first key with new default
final String testValue2 = Strings.repeat("A", 300);
settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE);
expectedMemUsage += (testValue2.length() - testValue1.length()) * 2;
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test updating first key without new default
final String testValue3 = Strings.repeat("A", 50);
settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE);
expectedMemUsage -= testValue2.length() - testValue3.length();
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test updating second key
settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE);
expectedMemUsage -= testValue1.length() - testValue2.length();
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test resetting key
settingsState.resetSettingLocked(testKey1);
expectedMemUsage += testValue2.length() - testValue3.length();
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test resetting default value
settingsState.resetSettingDefaultValueLocked(testKey1);
expectedMemUsage -= testValue2.length();
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test deletion
settingsState.deleteSettingLocked(testKey2);
expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */;
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test another package with a different key
final String testPackage2 = TEST_PACKAGE + "2";
final String testKey3 = SETTING_NAME + "3";
settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2);
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2;
assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
// Test system package
settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE);
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE));
// Test invalid value
try {
settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false,
TEST_PACKAGE);
fail("Should throw because it exceeded per package memory usage");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("You are adding too many system settings"));
}
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
// Test invalid key
try {
settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false,
TEST_PACKAGE);
fail("Should throw because it exceeded per package memory usage");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("You are adding too many system settings"));
}
assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
}
}

View File

@@ -23,8 +23,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.content.res.Configuration.Orientation;
import android.metrics.LogMaker;
import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,6 +75,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
@Nullable
private Consumer<Boolean> mMediaVisibilityChangedListener;
@Orientation
private int mLastOrientation;
private String mCachedSpecs = "";
@Nullable
@@ -88,21 +89,16 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
mQSLogger.logOnConfigurationChanged(
/* lastOrientation= */ mLastOrientation,
/* newOrientation= */ newConfig.orientation,
/* containerName= */ mView.getDumpableTag());
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
// Logging to aid the investigation of b/216244185.
Log.d(TAG,
"onConfigurationChange: "
+ "mShouldUseSplitNotificationShade="
+ mShouldUseSplitNotificationShade + ", "
+ "newConfig.windowConfiguration="
+ newConfig.windowConfiguration);
mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
mView.getDumpableTag());
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
switchTileLayout(false);
}
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
mLastOrientation = newConfig.orientation;
switchTileLayoutIfNeeded();
onConfigurationChanged();
}
};
@@ -334,6 +330,10 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
}
}
private void switchTileLayoutIfNeeded() {
switchTileLayout(/* force= */ false);
}
boolean switchTileLayout(boolean force) {
/* Whether or not the panel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();

View File

@@ -277,7 +277,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
// Then the layout changes
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
verify(mHorizontalLayoutListener).run(); // not invoked
verify(mHorizontalLayoutListener).run();
// When it is rotated back to portrait
mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
@@ -300,4 +300,24 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
verify(mQSTile).refreshState();
verify(mOtherTile, never()).refreshState();
}
@Test
public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() {
// Preconditions for horizontal layout
when(mMediaHost.getVisible()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
reset(mHorizontalLayoutListener);
// Only split shade status changes
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
// Horizontal layout is updated accordingly.
assertThat(mController.shouldUseHorizontalLayout()).isFalse();
verify(mHorizontalLayoutListener).run();
}
}

View File

@@ -657,25 +657,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState.mBindingServices.removeIf(filter);
userState.mCrashedServices.removeIf(filter);
final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
boolean anyServiceRemoved = false;
while (it.hasNext()) {
final ComponentName comp = it.next();
final String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices, userId);
// Update the touch exploration granted services setting.
userState.mTouchExplorationGrantedServices.remove(comp);
persistComponentNamesToSettingLocked(
Settings.Secure.
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
userState.mTouchExplorationGrantedServices, userId);
onUserStateChangedLocked(userState);
return;
anyServiceRemoved = true;
}
}
if (anyServiceRemoved) {
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices, userId);
// Update the touch exploration granted services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
userState.mTouchExplorationGrantedServices, userId);
onUserStateChangedLocked(userState);
}
}
}

View File

@@ -379,11 +379,16 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
resolvedType = key.requestResolvedType;
}
// Apply any launch flags from the ActivityOptions. This is to ensure that the caller
// can specify a consistent launch mode even if the PendingIntent is immutable
// Apply any launch flags from the ActivityOptions. This is used only by SystemUI
// to ensure that we can launch the pending intent with a consistent launch mode even
// if the provided PendingIntent is immutable (ie. to force an activity to launch into
// a new task, or to launch multiple instances if supported by the app)
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
// TODO(b/254490217): Move this check into SafeActivityOptions
if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
}
// Extract options before clearing calling identity

View File

@@ -1977,34 +1977,39 @@ public class NotificationManagerService extends SystemService {
return (haystack & needle) != 0;
}
public boolean isInLockDownMode() {
return mIsInLockDownMode;
// Return whether the user is in lockdown mode.
// If the flag is not set, we assume the user is not in lockdown.
public boolean isInLockDownMode(int userId) {
return mUserInLockDownMode.get(userId, false);
}
@Override
public synchronized void onStrongAuthRequiredChanged(int userId) {
boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mUserInLockDownMode.put(userId, userInLockDownModeNext);
boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1;
if (mIsInLockDownMode == isInLockDownModeNext) {
// Nothing happens if the lockdown mode of userId keeps the same.
if (userInLockDownModeNext == isInLockDownMode(userId)) {
return;
}
if (isInLockDownModeNext) {
cancelNotificationsWhenEnterLockDownMode();
// When the lockdown mode is changed, we perform the following steps.
// If the userInLockDownModeNext is true, all the function calls to
// notifyPostedLocked and notifyRemovedLocked will not be executed.
// The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked
// and postNotificationsWhenExitLockDownMode calls notifyPostedLocked.
// So we shall call cancelNotificationsWhenEnterLockDownMode before
// we set mUserInLockDownMode as true.
// On the other hand, if the userInLockDownModeNext is false, we shall call
// postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode
if (userInLockDownModeNext) {
cancelNotificationsWhenEnterLockDownMode(userId);
}
// When the mIsInLockDownMode is true, both notifyPostedLocked and
// notifyRemovedLocked will be dismissed. So we shall call
// cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode
// as true and call postNotificationsWhenExitLockDownMode after we set
// mIsInLockDownMode as false.
mIsInLockDownMode = isInLockDownModeNext;
mUserInLockDownMode.put(userId, userInLockDownModeNext);
if (!isInLockDownModeNext) {
postNotificationsWhenExitLockDownMode();
if (!userInLockDownModeNext) {
postNotificationsWhenExitLockDownMode(userId);
}
}
}
@@ -4953,16 +4958,7 @@ public class NotificationManagerService extends SystemService {
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
// If the caller is system, take the package name from the rule's owner rather than
// from the caller's package.
String rulePkg = pkg;
if (isCallingUidSystem()) {
if (automaticZenRule.getOwner() != null) {
rulePkg = automaticZenRule.getOwner().getPackageName();
}
}
return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
@@ -9713,11 +9709,14 @@ public class NotificationManagerService extends SystemService {
}
}
private void cancelNotificationsWhenEnterLockDownMode() {
private void cancelNotificationsWhenEnterLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
if (rec.getUser().getIdentifier() != userId) {
continue;
}
mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
rec.getStats());
}
@@ -9725,14 +9724,23 @@ public class NotificationManagerService extends SystemService {
}
}
private void postNotificationsWhenExitLockDownMode() {
private void postNotificationsWhenExitLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
// Set the delay to spread out the burst of notifications.
long delay = 0;
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
mListeners.notifyPostedLocked(rec, rec);
if (rec.getUser().getIdentifier() != userId) {
continue;
}
mHandler.postDelayed(() -> {
synchronized (mNotificationLock) {
mListeners.notifyPostedLocked(rec, rec);
}
}, delay);
delay += 20;
}
}
}
@@ -9911,6 +9919,9 @@ public class NotificationManagerService extends SystemService {
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (isInLockDownMode(record.getUser().getIdentifier())) {
continue;
}
if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
continue;
}
@@ -9952,8 +9963,8 @@ public class NotificationManagerService extends SystemService {
rankings.toArray(new NotificationListenerService.Ranking[0]));
}
boolean isInLockDownMode() {
return mStrongAuthTracker.isInLockDownMode();
boolean isInLockDownMode(int userId) {
return mStrongAuthTracker.isInLockDownMode(userId);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -11015,7 +11026,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
if (isInLockDownMode()) {
if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11121,7 +11132,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
if (isInLockDownMode()) {
if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11170,10 +11181,6 @@ public class NotificationManagerService extends SystemService {
*/
@GuardedBy("mNotificationLock")
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
if (isInLockDownMode()) {
return;
}
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
&& changedHiddenNotifications.size() > 0;
// TODO (b/73052211): if the ranking update changed the notification type,

View File

@@ -326,7 +326,7 @@ public class ZenModeHelper {
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
String reason) {
if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
component = getActivityInfo(automaticZenRule.getConfigurationActivity());
@@ -582,6 +582,11 @@ public class ZenModeHelper {
}
}
private boolean isSystemRule(AutomaticZenRule rule) {
return rule.getOwner() != null
&& ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
}
private ServiceInfo getServiceInfo(ComponentName owner) {
Intent queryIntent = new Intent();
queryIntent.setComponent(owner);

View File

@@ -644,8 +644,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
Permission bp = mRegistry.getPermission(info.name);
added = bp == null;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
enforcePermissionCapLocked(info, tree);
if (added) {
enforcePermissionCapLocked(info, tree);
bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {
throw new SecurityException("Not allowed to modify non-dynamic permission "
@@ -2135,6 +2135,46 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
/**
* If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and
* then updated past api 23, and the app does not satisfy any of the other SAW permission flags,
* the permission should be revoked.
*
* @param newPackage The new package that was installed
* @param oldPackage The old package that was updated
*/
private void revokeSystemAlertWindowIfUpgradedPast23(
@NonNull AndroidPackage newPackage,
@NonNull AndroidPackage oldPackage) {
if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M
|| newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M
|| !newPackage.getRequestedPermissions()
.contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) {
return;
}
Permission saw;
synchronized (mLock) {
saw = mRegistry.getPermission(Manifest.permission.SYSTEM_ALERT_WINDOW);
}
final PackageStateInternal ps =
mPackageManagerInt.getPackageStateInternal(newPackage.getPackageName());
if (shouldGrantPermissionByProtectionFlags(newPackage, ps, saw, new ArraySet<>())
|| shouldGrantPermissionBySignature(newPackage, saw)) {
return;
}
for (int userId : getAllUserIds()) {
try {
revokePermissionFromPackageForUser(newPackage.getPackageName(),
Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId,
mDefaultPermissionCallback);
} catch (IllegalStateException | SecurityException e) {
Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for "
+ newPackage.getPackageName() + " user " + userId, e);
}
}
}
/**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
@@ -4661,6 +4701,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
if (hasOldPkg) {
revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg);
revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg);
revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg);
}
if (hasPermissionDefinitionChanges) {
revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(

View File

@@ -98,7 +98,7 @@ class AppTaskImpl extends IAppTask.Stub {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
return mService.getRecentTasks().createRecentTaskInfo(task,
false /* stripExtras */);
false /* stripExtras */, true /* getTasksAllowed */);
} finally {
Binder.restoreCallingIdentity(origId);
}

View File

@@ -976,7 +976,7 @@ class RecentTasks {
continue;
}
res.add(createRecentTaskInfo(task, true /* stripExtras */));
res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
}
return res;
}
@@ -1895,7 +1895,8 @@ class RecentTasks {
/**
* Creates a new RecentTaskInfo from a Task.
*/
ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) {
ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras,
boolean getTasksAllowed) {
final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
// If the recent Task is detached, we consider it will be re-attached to the default
// TaskDisplayArea because we currently only support recent overview in the default TDA.
@@ -1907,6 +1908,9 @@ class RecentTasks {
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
if (!getTasksAllowed) {
Task.trimIneffectiveInfo(tr, rti);
}
// Fill in organized child task info for the task created by organizer.
if (tr.mCreatedByOrganizer) {

View File

@@ -177,6 +177,10 @@ class RunningTasks {
}
// Fill in some deprecated values
rti.id = rti.taskId;
if (!mAllowed) {
Task.trimIneffectiveInfo(task, rti);
}
return rti;
}
}

View File

@@ -3447,6 +3447,27 @@ class Task extends TaskFragment {
info.isSleeping = shouldSleepActivities();
}
/**
* Removes the activity info if the activity belongs to a different uid, which is
* different from the app that hosts the task.
*/
static void trimIneffectiveInfo(Task task, TaskInfo info) {
final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing,
false /* traverseTopToBottom */);
final int baseActivityUid =
baseActivity != null ? baseActivity.getUid() : task.effectiveUid;
if (info.topActivityInfo != null
&& task.effectiveUid != info.topActivityInfo.applicationInfo.uid) {
info.topActivity = null;
info.topActivityInfo = null;
}
if (task.effectiveUid != baseActivityUid) {
info.baseActivity = null;
}
}
@Nullable PictureInPictureParams getPictureInPictureParams() {
final Task topTask = getTopMostTask();
if (topTask == null) return null;

View File

@@ -435,63 +435,112 @@ public class NotificationListenersTest extends UiServiceTestCase {
@Test
public void testNotifyPostedLockedInLockdownMode() {
NotificationRecord r = mock(NotificationRecord.class);
NotificationRecord old = mock(NotificationRecord.class);
NotificationRecord r0 = mock(NotificationRecord.class);
NotificationRecord old0 = mock(NotificationRecord.class);
UserHandle uh0 = mock(UserHandle.class);
// before the lockdown mode
when(mNm.isInLockDownMode()).thenReturn(false);
mListeners.notifyPostedLocked(r, old, true);
mListeners.notifyPostedLocked(r, old, false);
verify(r, atLeast(2)).getSbn();
NotificationRecord r1 = mock(NotificationRecord.class);
NotificationRecord old1 = mock(NotificationRecord.class);
UserHandle uh1 = mock(UserHandle.class);
// in the lockdown mode
reset(r);
reset(old);
when(mNm.isInLockDownMode()).thenReturn(true);
mListeners.notifyPostedLocked(r, old, true);
mListeners.notifyPostedLocked(r, old, false);
verify(r, never()).getSbn();
}
// Neither user0 and user1 is in the lockdown mode
when(r0.getUser()).thenReturn(uh0);
when(uh0.getIdentifier()).thenReturn(0);
when(mNm.isInLockDownMode(0)).thenReturn(false);
@Test
public void testnotifyRankingUpdateLockedInLockdownMode() {
List chn = mock(List.class);
when(r1.getUser()).thenReturn(uh1);
when(uh1.getIdentifier()).thenReturn(1);
when(mNm.isInLockDownMode(1)).thenReturn(false);
// before the lockdown mode
when(mNm.isInLockDownMode()).thenReturn(false);
mListeners.notifyRankingUpdateLocked(chn);
verify(chn, atLeast(1)).size();
mListeners.notifyPostedLocked(r0, old0, true);
mListeners.notifyPostedLocked(r0, old0, false);
verify(r0, atLeast(2)).getSbn();
// in the lockdown mode
reset(chn);
when(mNm.isInLockDownMode()).thenReturn(true);
mListeners.notifyRankingUpdateLocked(chn);
verify(chn, never()).size();
mListeners.notifyPostedLocked(r1, old1, true);
mListeners.notifyPostedLocked(r1, old1, false);
verify(r1, atLeast(2)).getSbn();
// Reset
reset(r0);
reset(old0);
reset(r1);
reset(old1);
// Only user 0 is in the lockdown mode
when(r0.getUser()).thenReturn(uh0);
when(uh0.getIdentifier()).thenReturn(0);
when(mNm.isInLockDownMode(0)).thenReturn(true);
when(r1.getUser()).thenReturn(uh1);
when(uh1.getIdentifier()).thenReturn(1);
when(mNm.isInLockDownMode(1)).thenReturn(false);
mListeners.notifyPostedLocked(r0, old0, true);
mListeners.notifyPostedLocked(r0, old0, false);
verify(r0, never()).getSbn();
mListeners.notifyPostedLocked(r1, old1, true);
mListeners.notifyPostedLocked(r1, old1, false);
verify(r1, atLeast(2)).getSbn();
}
@Test
public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
NotificationRecord r = mock(NotificationRecord.class);
NotificationStats rs = mock(NotificationStats.class);
NotificationRecord r0 = mock(NotificationRecord.class);
NotificationStats rs0 = mock(NotificationStats.class);
UserHandle uh0 = mock(UserHandle.class);
NotificationRecord r1 = mock(NotificationRecord.class);
NotificationStats rs1 = mock(NotificationStats.class);
UserHandle uh1 = mock(UserHandle.class);
StatusBarNotification sbn = mock(StatusBarNotification.class);
FieldSetter.setField(mNm,
NotificationManagerService.class.getDeclaredField("mHandler"),
mock(NotificationManagerService.WorkerHandler.class));
// before the lockdown mode
when(mNm.isInLockDownMode()).thenReturn(false);
when(r.getSbn()).thenReturn(sbn);
mListeners.notifyRemovedLocked(r, 0, rs);
mListeners.notifyRemovedLocked(r, 0, rs);
verify(r, atLeast(2)).getSbn();
// Neither user0 and user1 is in the lockdown mode
when(r0.getUser()).thenReturn(uh0);
when(uh0.getIdentifier()).thenReturn(0);
when(mNm.isInLockDownMode(0)).thenReturn(false);
when(r0.getSbn()).thenReturn(sbn);
// in the lockdown mode
reset(r);
reset(rs);
when(mNm.isInLockDownMode()).thenReturn(true);
when(r.getSbn()).thenReturn(sbn);
mListeners.notifyRemovedLocked(r, 0, rs);
mListeners.notifyRemovedLocked(r, 0, rs);
verify(r, never()).getSbn();
when(r1.getUser()).thenReturn(uh1);
when(uh1.getIdentifier()).thenReturn(1);
when(mNm.isInLockDownMode(1)).thenReturn(false);
when(r1.getSbn()).thenReturn(sbn);
mListeners.notifyRemovedLocked(r0, 0, rs0);
mListeners.notifyRemovedLocked(r0, 0, rs0);
verify(r0, atLeast(2)).getSbn();
mListeners.notifyRemovedLocked(r1, 0, rs1);
mListeners.notifyRemovedLocked(r1, 0, rs1);
verify(r1, atLeast(2)).getSbn();
// Reset
reset(r0);
reset(rs0);
reset(r1);
reset(rs1);
// Only user 0 is in the lockdown mode
when(r0.getUser()).thenReturn(uh0);
when(uh0.getIdentifier()).thenReturn(0);
when(mNm.isInLockDownMode(0)).thenReturn(true);
when(r0.getSbn()).thenReturn(sbn);
when(r1.getUser()).thenReturn(uh1);
when(uh1.getIdentifier()).thenReturn(1);
when(mNm.isInLockDownMode(1)).thenReturn(false);
when(r1.getSbn()).thenReturn(sbn);
mListeners.notifyRemovedLocked(r0, 0, rs0);
mListeners.notifyRemovedLocked(r0, 0, rs0);
verify(r0, never()).getSbn();
mListeners.notifyRemovedLocked(r1, 0, rs1);
mListeners.notifyRemovedLocked(r1, 0, rs1);
verify(r1, atLeast(2)).getSbn();
}
}

View File

@@ -174,6 +174,7 @@ import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
@@ -7603,43 +7604,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
}
@Test
public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
mService.isSystemUid = true;
ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
.thenReturn(true);
mService.setZenHelper(mockZenModeHelper);
ComponentName owner = new ComponentName("android", "ProviderName");
ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
mBinderService.addAutomaticZenRule(rule, "com.android.settings");
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
}
@Test
public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
mService.isSystemUid = false;
ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
.thenReturn(true);
mService.setZenHelper(mockZenModeHelper);
ComponentName owner = new ComponentName("android", "ProviderName");
ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
mBinderService.addAutomaticZenRule(rule, "another.package");
// verify that zen mode helper gets passed in the package name from the arg, not the owner
verify(mockZenModeHelper).addAutomaticZenRule(
eq("another.package"), eq(rule), anyString());
}
@Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
@@ -9837,10 +9801,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
assertTrue(mStrongAuthTracker.isInLockDownMode());
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
assertFalse(mStrongAuthTracker.isInLockDownMode());
assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
}
@Test
@@ -9856,8 +9820,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// when entering the lockdown mode, cancel the 2 notifications.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
assertTrue(mStrongAuthTracker.isInLockDownMode());
mStrongAuthTracker.onStrongAuthRequiredChanged(0);
assertTrue(mStrongAuthTracker.isInLockDownMode(0));
// the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL.
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -9866,10 +9830,46 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// exit lockdown mode.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
mStrongAuthTracker.onStrongAuthRequiredChanged(0);
assertFalse(mStrongAuthTracker.isInLockDownMode(0));
// the notifyPostedLocked function is called twice.
verify(mListeners, times(2)).notifyPostedLocked(any(), any());
verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
//verify(mListeners, times(2)).notifyPostedLocked(any(), any());
}
@Test
public void testMakeRankingUpdateLockedInLockDownMode() {
// post 2 notifications from a same package
NotificationRecord pkgA = new NotificationRecord(mContext,
generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
mService.addNotification(pkgA);
NotificationRecord pkgB = new NotificationRecord(mContext,
generateSbn("a", 1000, 9, 1), mTestNotificationChannel);
mService.addNotification(pkgB);
mService.setIsVisibleToListenerReturnValue(true);
NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null);
assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
// when only user 0 entering the lockdown mode, its notification will be suppressed.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mStrongAuthTracker.onStrongAuthRequiredChanged(0);
assertTrue(mStrongAuthTracker.isInLockDownMode(0));
assertFalse(mStrongAuthTracker.isInLockDownMode(1));
nru = mService.makeRankingUpdateLocked(null);
assertEquals(1, nru.getRankingMap().getOrderedKeys().length);
// User 0 exits lockdown mode. Its notification will be resumed.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
mStrongAuthTracker.onStrongAuthRequiredChanged(0);
assertFalse(mStrongAuthTracker.isInLockDownMode(0));
assertFalse(mStrongAuthTracker.isInLockDownMode(1));
nru = mService.makeRankingUpdateLocked(null);
assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
}
@Test

View File

@@ -19,10 +19,12 @@ package com.android.server.notification;
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.service.notification.StatusBarNotification;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceIdSequence;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import java.util.HashSet;
import java.util.Set;
@@ -37,6 +39,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi
@Nullable
NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
@Nullable
Boolean mIsVisibleToListenerReturnValue = null;
TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
InstanceIdSequence notificationInstanceIdSequence) {
super(context, logger, notificationInstanceIdSequence);
@@ -119,6 +124,19 @@ public class TestableNotificationManagerService extends NotificationManagerServi
mShowReviewPermissionsNotification = setting;
}
protected void setIsVisibleToListenerReturnValue(boolean value) {
mIsVisibleToListenerReturnValue = value;
}
@Override
boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
ManagedServiceInfo listener) {
if (mIsVisibleToListenerReturnValue != null) {
return mIsVisibleToListenerReturnValue;
}
return super.isVisibleToListener(sbn, notificationType, listener);
}
public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
private int mGetStrongAuthForUserReturnValue = 0;
StrongAuthTrackerFake(Context context) {

View File

@@ -1671,36 +1671,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
}
@Test
public void testAddAutomaticZenRule_claimedSystemOwner() {
// Make sure anything that claims to have a "system" owner but not actually part of the
// system package still gets limited on number of rules
for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
ScheduleInfo si = new ScheduleInfo();
si.startHour = i;
AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
new ComponentName("android", "ScheduleConditionProvider" + i),
null, // configuration activity
ZenModeConfig.toScheduleConditionId(si),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
assertNotNull(id);
}
try {
AutomaticZenRule zenRule = new AutomaticZenRule("name",
new ComponentName("android", "ScheduleConditionProviderFinal"),
null, // configuration activity
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
}
}
@Test
public void testAddAutomaticZenRule_CA() {
AutomaticZenRule zenRule = new AutomaticZenRule("name",

View File

@@ -30,6 +30,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1220,20 +1221,34 @@ public class RecentTasksTest extends WindowTestsBase {
@Test
public void testCreateRecentTaskInfo_detachedTask() {
final Task task = createTaskBuilder(".Task").setCreateActivity(true).build();
final Task task = createTaskBuilder(".Task").build();
new ActivityBuilder(mSupervisor.mService)
.setTask(task)
.setUid(NOBODY_UID)
.setComponent(getUniqueComponentName())
.build();
final TaskDisplayArea tda = task.getDisplayArea();
assertTrue(task.isAttached());
assertTrue(task.supportsMultiWindow());
RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true);
RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
false /* getTasksAllowed */);
assertTrue(info.topActivity == null);
assertTrue(info.topActivityInfo == null);
assertTrue(info.baseActivity == null);
// The task can be put in split screen even if it is not attached now.
task.removeImmediately();
info = mRecentTasks.createRecentTaskInfo(task, true);
info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
@@ -1242,7 +1257,8 @@ public class RecentTasksTest extends WindowTestsBase {
doReturn(false).when(tda).supportsNonResizableMultiWindow();
doReturn(false).when(task).isResizeable();
info = mRecentTasks.createRecentTaskInfo(task, true);
info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
true /* getTasksAllowed */);
assertFalse(info.supportsMultiWindow);
@@ -1250,7 +1266,8 @@ public class RecentTasksTest extends WindowTestsBase {
// the device supports it.
doReturn(true).when(tda).supportsNonResizableMultiWindow();
info = mRecentTasks.createRecentTaskInfo(task, true);
info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
}