Merge TQ1A.230105.002 to aosp-master - DO NOT MERGE
Merged-In: I6c001e8ab8ef233c4e764d7c6753429b472dac94 Merged-In: If3c3110031ae04df59ed4e488714d187638d801a Change-Id: I200e0524debf868b3764affa35030a7831e7ecbe
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -177,6 +177,10 @@ class RunningTasks {
|
||||
}
|
||||
// Fill in some deprecated values
|
||||
rti.id = rti.taskId;
|
||||
|
||||
if (!mAllowed) {
|
||||
Task.trimIneffectiveInfo(task, rti);
|
||||
}
|
||||
return rti;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user