summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/AppOpsManager.java5
-rw-r--r--core/java/android/os/VintfObject.java2
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java110
-rw-r--r--core/jni/android_hardware_SoundTrigger.cpp10
-rw-r--r--core/jni/android_media_AudioTrack.cpp2
-rw-r--r--core/jni/android_view_InputChannel.cpp82
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java12
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java24
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java144
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java11
-rw-r--r--media/jni/android_media_MediaDataSource.cpp3
-rw-r--r--media/jni/android_media_MediaDescrambler.cpp5
-rw-r--r--media/jni/android_media_MediaHTTPConnection.cpp2
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp38
-rw-r--r--media/jni/soundpool/SoundPool.h2
-rw-r--r--media/tests/audiotests/shared_mem_test.cpp2
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java9
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java116
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java104
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java179
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java113
-rw-r--r--packages/SystemUI/res/drawable/biometric_dialog_bg.xml26
-rw-r--r--packages/SystemUI/res/layout/biometric_dialog.xml190
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java15
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java41
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java32
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderProxy.java126
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java16
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteService.java129
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java64
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java40
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java7
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java20
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java6
-rw-r--r--services/tests/mockingservicestests/Android.bp1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java364
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java37
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java14
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java72
-rw-r--r--tools/aapt2/cmd/Optimize.cpp50
-rw-r--r--tools/aapt2/cmd/Optimize.h15
-rw-r--r--tools/aapt2/cmd/Optimize_test.cpp68
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp14
-rw-r--r--tools/aapt2/format/binary/TableFlattener.h5
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp15
51 files changed, 1711 insertions, 707 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bb87d96b742c..9de42c3b57d5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1218,6 +1218,9 @@ public class AppOpsManager {
OP_REQUEST_INSTALL_PACKAGES,
OP_START_FOREGROUND,
OP_SMS_FINANCIAL_TRANSACTIONS,
+ OP_MANAGE_IPSEC_TUNNELS,
+ OP_GET_USAGE_STATS,
+ OP_INSTANT_APP_START_FOREGROUND
};
/**
@@ -1598,7 +1601,7 @@ public class AppOpsManager {
Manifest.permission.REQUEST_DELETE_PACKAGES,
Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
Manifest.permission.ACCEPT_HANDOVER,
- null, // no permission for OP_MANAGE_IPSEC_TUNNELS
+ Manifest.permission.MANAGE_IPSEC_TUNNELS,
Manifest.permission.FOREGROUND_SERVICE,
null, // no permission for OP_BLUETOOTH_SCAN
Manifest.permission.USE_BIOMETRIC,
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 23c54f450a67..1c78b081120a 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -72,6 +72,8 @@ public class VintfObject {
* ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0",
* "android.hardware.camera.device@3.2"]. There are no duplicates.
*
+ * For AIDL HALs, the version is stripped away
+ * (e.g. "android.hardware.light").
* @hide
*/
@TestApi
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 436897848f55..cae1f3831b4a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -24,6 +24,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -729,7 +730,13 @@ public class ChooserActivity extends ResolverActivity {
/**
* Returns true if app prediction service is defined and the component exists on device.
*/
- private boolean isAppPredictionServiceAvailable() {
+ @VisibleForTesting
+ public boolean isAppPredictionServiceAvailable() {
+ if (getPackageManager().getAppPredictionServicePackageName() == null) {
+ // Default AppPredictionService is not defined.
+ return false;
+ }
+
final String appPredictionServiceName =
getString(R.string.config_defaultAppPredictionService);
if (appPredictionServiceName == null) {
@@ -1584,25 +1591,19 @@ public class ChooserActivity extends ResolverActivity {
// ShareShortcutInfos directly.
boolean resultMessageSent = false;
for (int i = 0; i < driList.size(); i++) {
- List<ChooserTarget> chooserTargets = new ArrayList<>();
+ List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>();
for (int j = 0; j < resultList.size(); j++) {
if (driList.get(i).getResolvedComponentName().equals(
resultList.get(j).getTargetComponent())) {
- ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j);
- // Incoming results are ordered but without a score. Create a score
- // based on the index in order to be sorted appropriately when joined
- // with legacy direct share api results.
- float score = Math.max(1.0f - (0.05f * j), 0.0f);
- ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score);
- chooserTargets.add(chooserTarget);
- if (mDirectShareAppTargetCache != null && appTargets != null) {
- mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
- }
+ matchingShortcuts.add(resultList.get(j));
}
}
- if (chooserTargets.isEmpty()) {
+ if (matchingShortcuts.isEmpty()) {
continue;
}
+ List<ChooserTarget> chooserTargets = convertToChooserTarget(
+ matchingShortcuts, resultList, appTargets, shortcutType);
+
final Message msg = Message.obtain();
msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null);
@@ -1640,23 +1641,69 @@ public class ChooserActivity extends ResolverActivity {
return false;
}
- private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut,
- float score) {
- ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
- Bundle extras = new Bundle();
- extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
- return new ChooserTarget(
- // The name of this target.
- shortcutInfo.getShortLabel(),
- // Don't load the icon until it is selected to be shown
- null,
- // The ranking score for this target (0.0-1.0); the system will omit items with low
- // scores when there are too many Direct Share items.
- score,
- // The name of the component to be launched if this target is chosen.
- shareShortcut.getTargetComponent().clone(),
- // The extra values here will be merged into the Intent when this target is chosen.
- extras);
+ /**
+ * Converts a list of ShareShortcutInfos to ChooserTargets.
+ * @param matchingShortcuts List of shortcuts, all from the same package, that match the current
+ * share intent filter.
+ * @param allShortcuts List of all the shortcuts from all the packages on the device that are
+ * returned for the current sharing action.
+ * @param allAppTargets List of AppTargets. Null if the results are not from prediction service.
+ * @param shortcutType One of the values TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER or
+ * TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE
+ * @return A list of ChooserTargets sorted by score in descending order.
+ */
+ @VisibleForTesting
+ @NonNull
+ public List<ChooserTarget> convertToChooserTarget(
+ @NonNull List<ShortcutManager.ShareShortcutInfo> matchingShortcuts,
+ @NonNull List<ShortcutManager.ShareShortcutInfo> allShortcuts,
+ @Nullable List<AppTarget> allAppTargets, @ShareTargetType int shortcutType) {
+ // A set of distinct scores for the matched shortcuts. We use index of a rank in the sorted
+ // list instead of the actual rank value when converting a rank to a score.
+ List<Integer> scoreList = new ArrayList<>();
+ if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) {
+ for (int i = 0; i < matchingShortcuts.size(); i++) {
+ int shortcutRank = matchingShortcuts.get(i).getShortcutInfo().getRank();
+ if (!scoreList.contains(shortcutRank)) {
+ scoreList.add(shortcutRank);
+ }
+ }
+ Collections.sort(scoreList);
+ }
+
+ List<ChooserTarget> chooserTargetList = new ArrayList<>(matchingShortcuts.size());
+ for (int i = 0; i < matchingShortcuts.size(); i++) {
+ ShortcutInfo shortcutInfo = matchingShortcuts.get(i).getShortcutInfo();
+ int indexInAllShortcuts = allShortcuts.indexOf(matchingShortcuts.get(i));
+
+ float score;
+ if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
+ // Incoming results are ordered. Create a score based on index in the original list.
+ score = Math.max(1.0f - (0.01f * indexInAllShortcuts), 0.0f);
+ } else {
+ // Create a score based on the rank of the shortcut.
+ int rankIndex = scoreList.indexOf(shortcutInfo.getRank());
+ score = Math.max(1.0f - (0.01f * rankIndex), 0.0f);
+ }
+
+ Bundle extras = new Bundle();
+ extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
+ ChooserTarget chooserTarget = new ChooserTarget(shortcutInfo.getShortLabel(),
+ null, // Icon will be loaded later if this target is selected to be shown.
+ score, matchingShortcuts.get(i).getTargetComponent().clone(), extras);
+
+ chooserTargetList.add(chooserTarget);
+ if (mDirectShareAppTargetCache != null && allAppTargets != null) {
+ mDirectShareAppTargetCache.put(chooserTarget,
+ allAppTargets.get(indexInAllShortcuts));
+ }
+ }
+
+ // Sort ChooserTargets by score in descending order
+ Comparator<ChooserTarget> byScore =
+ (ChooserTarget a, ChooserTarget b) -> -Float.compare(a.getScore(), b.getScore());
+ Collections.sort(chooserTargetList, byScore);
+ return chooserTargetList;
}
private String convertServiceName(String packageName, String serviceName) {
@@ -1748,8 +1795,7 @@ public class ChooserActivity extends ResolverActivity {
if (!mIsAppPredictorComponentAvailable) {
return null;
}
- if (mAppPredictor == null
- && getPackageManager().getAppPredictionServicePackageName() != null) {
+ if (mAppPredictor == null) {
final IntentFilter filter = getTargetIntentFilter();
Bundle extras = new Bundle();
extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 03057dc5e602..0002f8b4048a 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -606,12 +606,12 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz,
goto exit;
}
memory = memoryDealer->allocate(offset + size);
- if (memory == 0 || memory->pointer() == NULL) {
+ if (memory == 0 || memory->unsecurePointer() == NULL) {
status = SOUNDTRIGGER_STATUS_ERROR;
goto exit;
}
- nSoundModel = (struct sound_trigger_sound_model *)memory->pointer();
+ nSoundModel = (struct sound_trigger_sound_model *)memory->unsecurePointer();
nSoundModel->type = type;
nSoundModel->uuid = nUuid;
@@ -737,18 +737,18 @@ android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
return SOUNDTRIGGER_STATUS_ERROR;
}
sp<IMemory> memory = memoryDealer->allocate(totalSize);
- if (memory == 0 || memory->pointer() == NULL) {
+ if (memory == 0 || memory->unsecurePointer() == NULL) {
return SOUNDTRIGGER_STATUS_ERROR;
}
if (dataSize != 0) {
- memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config),
+ memcpy((char *)memory->unsecurePointer() + sizeof(struct sound_trigger_recognition_config),
nData,
dataSize);
env->ReleaseByteArrayElements(jData, nData, 0);
}
env->DeleteLocalRef(jData);
struct sound_trigger_recognition_config *config =
- (struct sound_trigger_recognition_config *)memory->pointer();
+ (struct sound_trigger_recognition_config *)memory->unsecurePointer();
config->data_size = dataSize;
config->data_offset = sizeof(struct sound_trigger_recognition_config);
config->capture_requested = env->GetBooleanField(jConfig,
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index daa63475f706..c5049ecd3784 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -649,7 +649,7 @@ static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
- memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
+ memcpy(track->sharedBuffer()->unsecurePointer(), data + offsetInSamples, sizeInBytes);
written = sizeInBytes;
}
if (written >= 0) {
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 8553a2c24826..af34e7b7a7ff 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "InputChannel-JNI"
+#include "android-base/stringprintf.h"
#include <nativehelper/JNIHelp.h>
#include "nativehelper/scoped_utf_chars.h"
#include <android_runtime/AndroidRuntime.h>
@@ -60,7 +61,7 @@ private:
// ----------------------------------------------------------------------------
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
- mInputChannel(inputChannel), mDisposeCallback(NULL) {
+ mInputChannel(inputChannel), mDisposeCallback(nullptr) {
}
NativeInputChannel::~NativeInputChannel() {
@@ -74,8 +75,8 @@ void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callb
void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
if (mDisposeCallback) {
mDisposeCallback(env, obj, mInputChannel, mDisposeData);
- mDisposeCallback = NULL;
- mDisposeData = NULL;
+ mDisposeCallback = nullptr;
+ mDisposeData = nullptr;
}
}
@@ -96,14 +97,14 @@ static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject
sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
- return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
+ return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
}
void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
InputChannelObjDisposeCallback callback, void* data) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
- if (nativeInputChannel == NULL) {
+ if (nativeInputChannel == nullptr) {
ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
} else {
nativeInputChannel->setDisposeCallback(callback, data);
@@ -131,27 +132,27 @@ static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv*
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
- String8 message;
- message.appendFormat("Could not open input channel pair. status=%d", result);
- jniThrowRuntimeException(env, message.string());
- return NULL;
+ std::string message = android::base::StringPrintf(
+ "Could not open input channel pair : %s", strerror(-result));
+ jniThrowRuntimeException(env, message.c_str());
+ return nullptr;
}
- jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
+ jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);
if (env->ExceptionCheck()) {
- return NULL;
+ return nullptr;
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
- return NULL;
+ return nullptr;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
- return NULL;
+ return nullptr;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
@@ -170,7 +171,7 @@ static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jb
nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
- android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
delete nativeInputChannel;
}
}
@@ -179,14 +180,14 @@ static void android_view_InputChannel_nativeRelease(JNIEnv* env, jobject obj, jb
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
if (nativeInputChannel) {
- android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
delete nativeInputChannel;
}
}
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
- if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
+ if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
@@ -195,12 +196,12 @@ static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
- android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
}
static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
jobject parcelObj) {
- if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
+ if (android_view_InputChannel_getNativeInputChannel(env, obj) != nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"This object already has a native input channel.");
return;
@@ -222,25 +223,26 @@ static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject
static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
- if (parcel) {
- NativeInputChannel* nativeInputChannel =
- android_view_InputChannel_getNativeInputChannel(env, obj);
- if (nativeInputChannel) {
- sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
-
- parcel->writeInt32(1);
- inputChannel->write(*parcel);
- } else {
- parcel->writeInt32(0);
- }
+ if (parcel == nullptr) {
+ ALOGE("Could not obtain parcel for Java object");
+ return;
+ }
+
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (!nativeInputChannel) {
+ parcel->writeInt32(0); // not initialized
+ return;
}
+ parcel->writeInt32(1); // initialized
+ nativeInputChannel->getInputChannel()->write(*parcel);
}
static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
if (! nativeInputChannel) {
- return NULL;
+ return nullptr;
}
jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
@@ -250,10 +252,24 @@ static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj)
static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
- if (nativeInputChannel) {
- android_view_InputChannel_setNativeInputChannel(env, otherObj,
- new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
+ if (nativeInputChannel == nullptr) {
+ jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel");
+ return;
+ }
+
+ sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
+ if (inputChannel == nullptr) {
+ jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
+ return;
+ }
+ sp<InputChannel> dupInputChannel = inputChannel->dup();
+ if (dupInputChannel == nullptr) {
+ std::string message = android::base::StringPrintf(
+ "Could not duplicate input channel %s", inputChannel->getName().c_str());
+ jniThrowRuntimeException(env, message.c_str());
}
+ android_view_InputChannel_setNativeInputChannel(env, otherObj,
+ new NativeInputChannel(dupInputChannel));
}
static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj) {
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
index e1ccd7523eba..8891d3fd2dca 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
@@ -65,8 +65,10 @@ public class LegacyIntentClassificationFactoryTest {
null,
null,
null,
- 0,
- 0);
+ null,
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
InstrumentationRegistry.getContext(),
@@ -101,8 +103,10 @@ public class LegacyIntentClassificationFactoryTest {
null,
null,
null,
- 0,
- 0);
+ null,
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
InstrumentationRegistry.getContext(),
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
index b9a1a8cc4e42..bcea5fea6a13 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
@@ -83,9 +83,11 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
+ null,
createRemoteActionTemplates(),
- 0,
- 0);
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents =
mTemplateClassificationIntentFactory.create(
@@ -124,9 +126,11 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
+ null,
createRemoteActionTemplates(),
- 0,
- 0);
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents =
mTemplateClassificationIntentFactory.create(
@@ -162,8 +166,10 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
- 0,
- 0);
+ null,
+ 0L,
+ 0L,
+ 0d);
mTemplateClassificationIntentFactory.create(
InstrumentationRegistry.getContext(),
@@ -196,9 +202,11 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
+ null,
new RemoteActionTemplate[0],
- 0,
- 0);
+ 0L,
+ 0L,
+ 0d);
mTemplateClassificationIntentFactory.create(
InstrumentationRegistry.getContext(),
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 67036fef30ba..c44b7d81868d 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -36,6 +36,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -51,6 +52,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -807,6 +810,108 @@ public class ChooserActivityTest {
is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
}
+ /**
+ * The case when AppPrediction service is not defined in PackageManager is already covered
+ * as a test parameter {@link ChooserActivityTest#packageManagers}. This test is checking the
+ * case when the prediction service is defined but the component is not available on the device.
+ */
+ @Test
+ public void testIsAppPredictionServiceAvailable() {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ if (activity.getPackageManager().getAppPredictionServicePackageName() == null) {
+ assertThat(activity.isAppPredictionServiceAvailable(), is(false));
+ } else {
+ assertThat(activity.isAppPredictionServiceAvailable(), is(true));
+
+ sOverrides.resources = Mockito.spy(activity.getResources());
+ when(sOverrides.resources.getString(R.string.config_defaultAppPredictionService))
+ .thenReturn("ComponentNameThatDoesNotExist");
+
+ assertThat(activity.isAppPredictionServiceAvailable(), is(false));
+ }
+ }
+
+ @Test
+ public void testConvertToChooserTarget_predictionService() {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
+
+ int[] expectedOrderAllShortcuts = {0, 1, 2, 3};
+ float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.98f, 0.97f};
+
+ List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
+ null, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderAllShortcuts, expectedScoreAllShortcuts);
+
+ List<ShareShortcutInfo> subset = new ArrayList<>();
+ subset.add(shortcuts.get(1));
+ subset.add(shortcuts.get(2));
+ subset.add(shortcuts.get(3));
+
+ int[] expectedOrderSubset = {1, 2, 3};
+ float[] expectedScoreSubset = {0.99f, 0.98f, 0.97f};
+
+ chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null,
+ TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderSubset, expectedScoreSubset);
+ }
+
+ @Test
+ public void testConvertToChooserTarget_shortcutManager() {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
+
+ int[] expectedOrderAllShortcuts = {2, 0, 3, 1};
+ float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.99f, 0.98f};
+
+ List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
+ null, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderAllShortcuts, expectedScoreAllShortcuts);
+
+ List<ShareShortcutInfo> subset = new ArrayList<>();
+ subset.add(shortcuts.get(1));
+ subset.add(shortcuts.get(2));
+ subset.add(shortcuts.get(3));
+
+ int[] expectedOrderSubset = {2, 3, 1};
+ float[] expectedScoreSubset = {1.0f, 0.99f, 0.98f};
+
+ chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null,
+ TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderSubset, expectedScoreSubset);
+ }
+
// This test is too long and too slow and should not be taken as an example for future tests.
@Test
public void testDirectTargetSelectionLogging() throws InterruptedException {
@@ -1103,4 +1208,43 @@ public class ChooserActivityTest {
return bitmap;
}
+
+ private List<ShareShortcutInfo> createShortcuts(Context context) {
+ Intent testIntent = new Intent("TestIntent");
+
+ List<ShareShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut1")
+ .setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2
+ new ComponentName("package1", "class1")));
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut2")
+ .setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3
+ new ComponentName("package2", "class2")));
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut3")
+ .setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0
+ new ComponentName("package3", "class3")));
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut4")
+ .setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2
+ new ComponentName("package4", "class4")));
+
+ return shortcuts;
+ }
+
+ private void assertCorrectShortcutToChooserTargetConversion(List<ShareShortcutInfo> shortcuts,
+ List<ChooserTarget> chooserTargets, int[] expectedOrder, float[] expectedScores) {
+ assertEquals(expectedOrder.length, chooserTargets.size());
+ for (int i = 0; i < chooserTargets.size(); i++) {
+ ChooserTarget ct = chooserTargets.get(i);
+ ShortcutInfo si = shortcuts.get(expectedOrder[i]).getShortcutInfo();
+ ComponentName cn = shortcuts.get(expectedOrder[i]).getTargetComponent();
+
+ assertEquals(si.getId(), ct.getIntentExtras().getString(Intent.EXTRA_SHORTCUT_ID));
+ assertEquals(si.getShortLabel(), ct.getTitle());
+ assertThat(Math.abs(expectedScores[i] - ct.getScore()) < 0.000001, is(true));
+ assertEquals(cn.flattenToString(), ct.getComponentName().flattenToString());
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 44e56eaf0593..1d567c73f376 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -85,6 +86,14 @@ public class ChooserWrapperActivity extends ChooserActivity {
}
@Override
+ public Resources getResources() {
+ if (sOverrides.resources != null) {
+ return sOverrides.resources;
+ }
+ return super.getResources();
+ }
+
+ @Override
protected Bitmap loadThumbnail(Uri uri, Size size) {
if (sOverrides.previewThumbnail != null) {
return sOverrides.previewThumbnail;
@@ -145,6 +154,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
public Bitmap previewThumbnail;
public MetricsLogger metricsLogger;
public int alternateProfileSetting;
+ public Resources resources;
public void reset() {
onSafelyStartCallback = null;
@@ -157,6 +167,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
resolverListController = mock(ResolverListController.class);
metricsLogger = mock(MetricsLogger.class);
alternateProfileSetting = 0;
+ resources = null;
}
}
}
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
index 8c38d887f82b..9705b91dd52a 100644
--- a/media/jni/android_media_MediaDataSource.cpp
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -106,7 +106,8 @@ ssize_t JMediaDataSource::readAt(off64_t offset, size_t size) {
}
ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
- env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)mMemory->pointer());
+ env->GetByteArrayRegion(mByteArrayObj, 0, numread,
+ (jbyte*)mMemory->unsecurePointer());
return numread;
}
diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp
index aa79ce0a44ab..c61365a448d3 100644
--- a/media/jni/android_media_MediaDescrambler.cpp
+++ b/media/jni/android_media_MediaDescrambler.cpp
@@ -220,7 +220,7 @@ status_t JDescrambler::descramble(
return NO_MEMORY;
}
- memcpy(mMem->pointer(),
+ memcpy(mMem->unsecurePointer(),
(const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
DestinationBuffer dstBuffer;
@@ -248,7 +248,8 @@ status_t JDescrambler::descramble(
if (*status == Status::OK) {
if (*bytesWritten > 0 && (ssize_t) *bytesWritten <= totalLength) {
- memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *bytesWritten);
+ memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->unsecurePointer(),
+ *bytesWritten);
} else {
// status seems OK but bytesWritten is invalid, we really
// have no idea what is wrong.
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index 365e045689f0..53adff3e251e 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -148,7 +148,7 @@ static jint android_media_MediaHTTPConnection_native_readAt(
byteArrayObj,
0,
n,
- (jbyte *)conn->getIMemory()->pointer());
+ (jbyte *)conn->getIMemory()->unsecurePointer());
}
return n;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 3809bc4752a8..18fd1a01cfd6 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -396,8 +396,12 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
}
if (videoFrame == NULL) {
ALOGE("getFrameAtTime: videoFrame is a NULL pointer");
@@ -423,7 +427,11 @@ static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
VideoFrame *videoFrame = NULL;
sp<IMemory> frameMemory = retriever->getImageAtIndex(index, colorFormat);
if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
}
if (videoFrame == NULL) {
ALOGE("getImageAtIndex: videoFrame is a NULL pointer");
@@ -454,7 +462,11 @@ static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex(
sp<IMemory> frameMemory = retriever->getImageAtIndex(
index, colorFormat, true /*metaOnly*/, true /*thumbnail*/);
if (frameMemory != 0) {
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
int32_t thumbWidth = videoFrame->mWidth;
int32_t thumbHeight = videoFrame->mHeight;
videoFrame = NULL;
@@ -467,7 +479,11 @@ static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex(
|| thumbPixels * 6 >= maxPixels) {
frameMemory = retriever->getImageAtIndex(
index, colorFormat, false /*metaOnly*/, true /*thumbnail*/);
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
if (thumbPixels > maxPixels) {
int downscale = ceil(sqrt(thumbPixels / (float)maxPixels));
@@ -514,11 +530,15 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtIndex(
size_t i = 0;
for (; i < numFrames; i++) {
sp<IMemory> frame = retriever->getFrameAtIndex(frameIndex + i, colorFormat);
- if (frame == NULL || frame->pointer() == NULL) {
+ if (frame == NULL || frame->unsecurePointer() == NULL) {
ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i);
break;
}
- VideoFrame *videoFrame = static_cast<VideoFrame *>(frame->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame *videoFrame = static_cast<VideoFrame *>(frame->unsecurePointer());
jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
env->CallBooleanMethod(arrayList, fields.arrayListAdd, bitmapObj);
env->DeleteLocalRef(bitmapObj);
@@ -551,7 +571,11 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
// the method name to getEmbeddedPicture().
sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object
- mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->unsecurePointer());
}
if (mediaAlbumArt == NULL) {
ALOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 9d7410305c2c..01e4faae6f6c 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -61,7 +61,7 @@ public:
audio_channel_mask_t channelMask() { return mChannelMask; }
size_t size() { return mSize; }
int state() { return mState; }
- uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
+ uint8_t* data() { return static_cast<uint8_t*>(mData->unsecurePointer()); }
status_t doLoad();
void startLoad() { mState = LOADING; }
sp<IMemory> getIMemory() { return mData; }
diff --git a/media/tests/audiotests/shared_mem_test.cpp b/media/tests/audiotests/shared_mem_test.cpp
index 2f5749933b54..d586b6a6da17 100644
--- a/media/tests/audiotests/shared_mem_test.cpp
+++ b/media/tests/audiotests/shared_mem_test.cpp
@@ -92,7 +92,7 @@ int AudioTrackTest::Test01() {
iMem = heap->allocate(BUF_SZ*sizeof(short));
- p = static_cast<uint8_t*>(iMem->pointer());
+ p = static_cast<uint8_t*>(iMem->unsecurePointer());
memcpy(p, smpBuf, BUF_SZ*sizeof(short));
sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
index 2010620f76ed..033f1b10118c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
@@ -30,6 +30,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.security.KeyStoreException;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
/**
* State about encrypted backups that needs to be remembered.
@@ -51,6 +52,9 @@ public class CryptoSettings {
SECONDARY_KEY_LAST_ROTATED_AT
};
+ private static final long DEFAULT_SECONDARY_KEY_ROTATION_PERIOD =
+ TimeUnit.MILLISECONDS.convert(31, TimeUnit.DAYS);
+
private static final String KEY_ANCESTRAL_SECONDARY_KEY_VERSION =
"ancestral_secondary_key_version";
@@ -202,6 +206,11 @@ public class CryptoSettings {
.apply();
}
+ /** The number of milliseconds between secondary key rotation */
+ public long backupSecondaryKeyRotationIntervalMs() {
+ return DEFAULT_SECONDARY_KEY_ROTATION_PERIOD;
+ }
+
/** Deletes all crypto settings related to backup (as opposed to restore). */
public void clearAllSettingsForBackup() {
Editor sharedPrefsEditor = mSharedPreferences.edit();
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java
new file mode 100644
index 000000000000..91b57cf69795
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask;
+
+import java.io.File;
+import java.time.Clock;
+import java.util.Optional;
+
+/**
+ * Helps schedule rotations of secondary keys.
+ *
+ * <p>TODO(b/72028016) Replace with a job.
+ */
+public class SecondaryKeyRotationScheduler {
+
+ private static final String TAG = "SecondaryKeyRotationScheduler";
+ private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation";
+
+ private final Context mContext;
+ private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ private final CryptoSettings mCryptoSettings;
+ private final Clock mClock;
+
+ public SecondaryKeyRotationScheduler(
+ Context context,
+ RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
+ CryptoSettings cryptoSettings,
+ Clock clock) {
+ mContext = context;
+ mCryptoSettings = cryptoSettings;
+ mClock = clock;
+ mSecondaryKeyManager = secondaryKeyManager;
+ }
+
+ /**
+ * Returns {@code true} if a sentinel file for forcing secondary key rotation is present. This
+ * is only for testing purposes.
+ */
+ private boolean isForceRotationTestSentinelPresent() {
+ File file = new File(mContext.getFilesDir(), SENTINEL_FILE_PATH);
+ if (file.exists()) {
+ file.delete();
+ return true;
+ }
+ return false;
+ }
+
+ /** Start the key rotation task if it's time to do so */
+ public void startRotationIfScheduled() {
+ if (isForceRotationTestSentinelPresent()) {
+ Slog.i(TAG, "Found force flag for secondary rotation. Starting now.");
+ startRotation();
+ return;
+ }
+
+ Optional<Long> maybeLastRotated = mCryptoSettings.getSecondaryLastRotated();
+ if (!maybeLastRotated.isPresent()) {
+ Slog.v(TAG, "No previous rotation, scheduling from now.");
+ scheduleRotationFromNow();
+ return;
+ }
+
+ long lastRotated = maybeLastRotated.get();
+ long now = mClock.millis();
+
+ if (lastRotated > now) {
+ Slog.i(TAG, "Last rotation was in the future. Clock must have changed. Rotate now.");
+ startRotation();
+ return;
+ }
+
+ long millisSinceLastRotation = now - lastRotated;
+ long rotationInterval = mCryptoSettings.backupSecondaryKeyRotationIntervalMs();
+ if (millisSinceLastRotation >= rotationInterval) {
+ Slog.i(
+ TAG,
+ "Last rotation was more than "
+ + rotationInterval
+ + "ms ("
+ + millisSinceLastRotation
+ + "ms) in the past. Rotate now.");
+ startRotation();
+ }
+
+ Slog.v(TAG, "No rotation required, last " + lastRotated + ".");
+ }
+
+ private void startRotation() {
+ scheduleRotationFromNow();
+ new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager).run();
+ }
+
+ private void scheduleRotationFromNow() {
+ mCryptoSettings.setSecondaryLastRotated(mClock.millis());
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
new file mode 100644
index 000000000000..77cfded32173
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+/**
+ * Starts rotating to a new secondary key. Cannot complete until the screen is unlocked and the new
+ * key is synced.
+ */
+public class StartSecondaryKeyRotationTask {
+ private static final String TAG = "BE-StSecondaryKeyRotTsk";
+
+ private final CryptoSettings mCryptoSettings;
+ private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+
+ public StartSecondaryKeyRotationTask(
+ CryptoSettings cryptoSettings,
+ RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager) {
+ mCryptoSettings = Preconditions.checkNotNull(cryptoSettings);
+ mSecondaryKeyManager = Preconditions.checkNotNull(secondaryKeyManager);
+ }
+
+ /** Begin the key rotation */
+ public void run() {
+ Slog.i(TAG, "Attempting to initiate a secondary key rotation.");
+
+ Optional<String> maybeCurrentAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
+ if (!maybeCurrentAlias.isPresent()) {
+ Slog.w(TAG, "No active current alias. Cannot trigger a secondary rotation.");
+ return;
+ }
+ String currentAlias = maybeCurrentAlias.get();
+
+ Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias();
+ if (maybeNextAlias.isPresent()) {
+ String nextAlias = maybeNextAlias.get();
+ if (nextAlias.equals(currentAlias)) {
+ // Shouldn't be possible, but guard against accidentally deleting the active key.
+ Slog.e(TAG, "Was already trying to rotate to what is already the active key.");
+ } else {
+ Slog.w(TAG, "Was already rotating to another key. Cancelling that.");
+ try {
+ mSecondaryKeyManager.remove(nextAlias);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Could not remove old key", e);
+ }
+ }
+ mCryptoSettings.removeNextSecondaryKeyAlias();
+ }
+
+ RecoverableKeyStoreSecondaryKey newSecondaryKey;
+ try {
+ newSecondaryKey = mSecondaryKeyManager.generate();
+ } catch (LockScreenRequiredException e) {
+ Slog.e(TAG, "No lock screen is set - cannot generate a new key to rotate to.", e);
+ return;
+ } catch (InternalRecoveryServiceException e) {
+ Slog.e(TAG, "Internal error in Recovery Controller, failed to rotate key.", e);
+ return;
+ } catch (UnrecoverableKeyException e) {
+ Slog.e(TAG, "Failed to get key after generating, failed to rotate", e);
+ return;
+ }
+
+ String alias = newSecondaryKey.getAlias();
+ Slog.i(TAG, "Generated a new secondary key with alias '" + alias + "'.");
+ try {
+ mCryptoSettings.setNextSecondaryAlias(alias);
+ Slog.i(TAG, "Successfully set '" + alias + "' as next key to rotate to");
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unexpected error setting next alias", e);
+ try {
+ mSecondaryKeyManager.remove(alias);
+ } catch (Exception err) {
+ Slog.wtf(TAG, "Failed to remove generated key after encountering error", err);
+ }
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java
new file mode 100644
index 000000000000..c31d19d8568c
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.io.File;
+import java.time.Clock;
+
+@Config(shadows = SecondaryKeyRotationSchedulerTest.ShadowStartSecondaryKeyRotationTask.class)
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class SecondaryKeyRotationSchedulerTest {
+ private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation";
+
+ @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ @Mock private Clock mClock;
+
+ private CryptoSettings mCryptoSettings;
+ private SecondaryKeyRotationScheduler mScheduler;
+ private long mRotationIntervalMillis;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context application = ApplicationProvider.getApplicationContext();
+
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(application);
+ mRotationIntervalMillis = mCryptoSettings.backupSecondaryKeyRotationIntervalMs();
+
+ mScheduler =
+ new SecondaryKeyRotationScheduler(
+ application, mSecondaryKeyManager, mCryptoSettings, mClock);
+ ShadowStartSecondaryKeyRotationTask.reset();
+ }
+
+ @Test
+ public void startRotationIfScheduled_rotatesIfRotationWasFarEnoughInThePast() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated + mRotationIntervalMillis);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
+ }
+
+ @Test
+ public void startRotationIfScheduled_setsNewRotationTimeIfRotationWasFarEnoughInThePast() {
+ long lastRotated = 100009;
+ long now = lastRotated + mRotationIntervalMillis;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(now);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
+ }
+
+ @Test
+ public void startRotationIfScheduled_rotatesIfClockHasChanged() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated - 1);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
+ }
+
+ @Test
+ public void startRotationIfScheduled_rotatesIfSentinelFileIsPresent() throws Exception {
+ File file = new File(RuntimeEnvironment.application.getFilesDir(), SENTINEL_FILE_PATH);
+ file.createNewFile();
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
+ }
+
+ @Test
+ public void startRotationIfScheduled_setsNextRotationIfClockHasChanged() {
+ long lastRotated = 100009;
+ long now = lastRotated - 1;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(now);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
+ }
+
+ @Test
+ public void startRotationIfScheduled_doesNothingIfRotationWasRecentEnough() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated + mRotationIntervalMillis - 1);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isFalse();
+ }
+
+ @Test
+ public void startRotationIfScheduled_doesNotSetRotationTimeIfRotationWasRecentEnough() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated + mRotationIntervalMillis - 1);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(lastRotated);
+ }
+
+ @Test
+ public void startRotationIfScheduled_setsLastRotatedToNowIfNeverRotated() {
+ long now = 13295436;
+ setNow(now);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
+ }
+
+ private void setNow(long timestamp) {
+ when(mClock.millis()).thenReturn(timestamp);
+ }
+
+ @Implements(StartSecondaryKeyRotationTask.class)
+ public static class ShadowStartSecondaryKeyRotationTask {
+ private static boolean sRan = false;
+
+ @Implementation
+ public void run() {
+ sRan = true;
+ }
+
+ @Resetter
+ public static void reset() {
+ sRan = false;
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java
new file mode 100644
index 000000000000..4ac4fa8d06c9
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.security.SecureRandom;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowRecoveryController.class})
+@Presubmit
+public class StartSecondaryKeyRotationTaskTest {
+
+ private CryptoSettings mCryptoSettings;
+ private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ private StartSecondaryKeyRotationTask mStartSecondaryKeyRotationTask;
+
+ @Before
+ public void setUp() throws Exception {
+ mSecondaryKeyManager =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(RuntimeEnvironment.application),
+ new SecureRandom());
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(RuntimeEnvironment.application);
+ mStartSecondaryKeyRotationTask =
+ new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager);
+
+ ShadowRecoveryController.reset();
+ }
+
+ @Test
+ public void run_doesNothingIfNoActiveSecondaryExists() {
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
+ }
+
+ @Test
+ public void run_doesNotRemoveExistingNextSecondaryKeyIfItIsAlreadyActive() throws Exception {
+ generateAnActiveKey();
+ String activeAlias = mCryptoSettings.getActiveSecondaryKeyAlias().get();
+ mCryptoSettings.setNextSecondaryAlias(activeAlias);
+
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mSecondaryKeyManager.get(activeAlias).isPresent()).isTrue();
+ }
+
+ @Test
+ public void run_doesRemoveExistingNextSecondaryKeyIfItIsNotYetActive() throws Exception {
+ generateAnActiveKey();
+ RecoverableKeyStoreSecondaryKey nextKey = mSecondaryKeyManager.generate();
+ String nextAlias = nextKey.getAlias();
+ mCryptoSettings.setNextSecondaryAlias(nextAlias);
+
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isFalse();
+ }
+
+ @Test
+ public void run_generatesANewNextSecondaryKey() throws Exception {
+ generateAnActiveKey();
+
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isTrue();
+ }
+
+ @Test
+ public void run_generatesANewKeyThatExistsInKeyStore() throws Exception {
+ generateAnActiveKey();
+
+ mStartSecondaryKeyRotationTask.run();
+
+ String nextAlias = mCryptoSettings.getNextSecondaryKeyAlias().get();
+ assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isTrue();
+ }
+
+ private void generateAnActiveKey() throws Exception {
+ RecoverableKeyStoreSecondaryKey secondaryKey = mSecondaryKeyManager.generate();
+ mCryptoSettings.setActiveSecondaryKeyAlias(secondaryKey.getAlias());
+ }
+}
diff --git a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
deleted file mode 100644
index 0c6d57dd6183..000000000000
--- a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:topLeftRadius="@dimen/biometric_dialog_corner_size"
- android:topRightRadius="@dimen/biometric_dialog_corner_size"
- android:bottomLeftRadius="@dimen/biometric_dialog_corner_size"
- android:bottomRightRadius="@dimen/biometric_dialog_corner_size"/>
-</shape>
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
deleted file mode 100644
index e687cdfac356..000000000000
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="center" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="bottom"
- android:background="@color/biometric_dialog_dim_color"
- android:orientation="vertical">
-
- <!-- This is not a Space since Spaces cannot be clicked -->
- <View
- android:id="@+id/space"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:contentDescription="@string/biometric_dialog_empty_space_description"/>
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <!-- This is not a Space since Spaces cannot be clicked. The width of this changes
- depending on horizontal/portrait orientation -->
- <View
- android:id="@+id/left_space"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:contentDescription="@string/biometric_dialog_empty_space_description"/>
-
- <LinearLayout
- android:id="@+id/dialog"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/biometric_dialog_bg"
- android:layout_marginBottom="@dimen/biometric_dialog_border_padding"
- android:layout_marginLeft="@dimen/biometric_dialog_border_padding"
- android:layout_marginRight="@dimen/biometric_dialog_border_padding">
-
- <TextView
- android:id="@+id/title"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:layout_marginTop="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:textSize="20sp"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:paddingTop="8dp"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <ImageView
- android:id="@+id/biometric_icon"
- android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="48dp"
- android:scaleType="fitXY" />
-
- <TextView
- android:id="@+id/error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:paddingTop="16dp"
- android:paddingBottom="24dp"
- android:textSize="12sp"
- android:gravity="center_horizontal"
- android:accessibilityLiveRegion="polite"
- android:textColor="@color/biometric_dialog_gray"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="72dip"
- android:paddingTop="24dp"
- android:layout_gravity="center_vertical"
- style="?android:attr/buttonBarStyle"
- android:orientation="horizontal"
- android:measureWithLargestChild="true">
- <Space android:id="@+id/leftSpacer"
- android:layout_width="12dp"
- android:layout_height="match_parent"
- android:visibility="visible" />
- <!-- Negative Button -->
- <Button android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:gravity="center"
- android:maxLines="2" />
- <Space android:id="@+id/middleSpacer"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:visibility="visible" />
- <!-- Positive Button -->
- <Button android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="gone"/>
- <!-- Try Again Button -->
- <Button android:id="@+id/button_try_again"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="gone"/>
- <Space android:id="@+id/rightSpacer"
- android:layout_width="12dip"
- android:layout_height="match_parent"
- android:visibility="visible" />
- </LinearLayout>
- </LinearLayout>
-
- <!-- This is not a Space since Spaces cannot be clicked. The width of this changes
- depending on horizontal/portrait orientation -->
- <View
- android:id="@+id/right_space"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:contentDescription="@string/biometric_dialog_empty_space_description"/>
-
- </LinearLayout>
-
- </ScrollView>
-
- </LinearLayout>
-
-</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 8c95b844ae2f..f853b638db46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -503,8 +503,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
* device is dozing when the light sensor is on.
*/
public void setAodFrontScrimAlpha(float alpha) {
- if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()
- && mInFrontAlpha != alpha) {
+ if (((mState == ScrimState.AOD && mDozeParameters.getAlwaysOn())
+ || mState == ScrimState.PULSING) && mInFrontAlpha != alpha) {
mInFrontAlpha = alpha;
updateScrims();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 2623b46ba50f..5d3cdc88aa99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -243,7 +243,7 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void transitionToPulsing() {
+ public void transitionToPulsing_withFrontAlphaUpdates() {
// Pre-condition
// Need to go to AoD first because PULSING doesn't change
// the back scrim opacity - otherwise it would hide AoD wallpapers.
@@ -267,11 +267,22 @@ public class ScrimControllerTest extends SysuiTestCase {
true /* behind */,
false /* bubble */);
+ // ... and when ambient goes dark, front scrim should be semi-transparent
+ mScrimController.setAodFrontScrimAlpha(0.5f);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be semi-transparent
+ assertScrimAlpha(SEMI_TRANSPARENT /* front */,
+ OPAQUE /* back */,
+ TRANSPARENT /* bubble */);
+
mScrimController.setWakeLockScreenSensorActive(true);
mScrimController.finishAnimationsImmediately();
- assertScrimAlpha(TRANSPARENT /* front */,
+ assertScrimAlpha(SEMI_TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
+
+ // Reset value since enums are static.
+ mScrimController.setAodFrontScrimAlpha(0f);
}
@Test
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 548665ba3a32..8c60f1aa439b 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -34,6 +34,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.Xml;
@@ -86,6 +87,8 @@ public class PackageWatchdog {
// Number of package failures within the duration above before we notify observers
@VisibleForTesting
static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5;
+ @VisibleForTesting
+ static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
// Whether explicit health checks are enabled or not
private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true;
@@ -224,8 +227,10 @@ public class PackageWatchdog {
* check state will be reset to a default depending on if the package is contained in
* {@link mPackagesWithExplicitHealthCheckEnabled}.
*
- * @throws IllegalArgumentException if {@code packageNames} is empty
- * or {@code durationMs} is less than 1
+ * <p>If {@code packageNames} is empty, this will be a no-op.
+ *
+ * <p>If {@code durationMs} is less than 1, a default monitoring duration
+ * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
*/
public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
long durationMs) {
@@ -234,9 +239,9 @@ public class PackageWatchdog {
return;
}
if (durationMs < 1) {
- // TODO: Instead of failing, monitor for default? 48hrs?
- throw new IllegalArgumentException("Invalid duration " + durationMs + "ms for observer "
+ Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer "
+ observer.getName() + ". Not observing packages " + packageNames);
+ durationMs = DEFAULT_OBSERVING_DURATION_MS;
}
List<MonitoredPackage> packages = new ArrayList<>();
@@ -969,6 +974,9 @@ public class PackageWatchdog {
class MonitoredPackage {
//TODO(b/120598832): VersionedPackage?
private final String mName;
+ // Times when package failures happen sorted in ascending order
+ @GuardedBy("mLock")
+ private final LongArrayQueue mFailureHistory = new LongArrayQueue();
// One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after
// methods that could change the health check state: handleElapsedTimeLocked and
// tryPassHealthCheckLocked
@@ -988,12 +996,6 @@ public class PackageWatchdog {
// of the package, see #getHealthCheckStateLocked
@GuardedBy("mLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
- // System uptime of first package failure
- @GuardedBy("mLock")
- private long mUptimeStartMs;
- // Number of failures since mUptimeStartMs
- @GuardedBy("mLock")
- private int mFailures;
MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) {
this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
@@ -1028,20 +1030,17 @@ public class PackageWatchdog {
*/
@GuardedBy("mLock")
public boolean onFailureLocked() {
+ // Sliding window algorithm: find out if there exists a window containing failures >=
+ // mTriggerFailureCount.
final long now = mSystemClock.uptimeMillis();
- final long duration = now - mUptimeStartMs;
- if (duration > mTriggerFailureDurationMs) {
- // TODO(b/120598832): Reseting to 1 is not correct
- // because there may be more than 1 failure in the last trigger window from now
- // This is the RescueParty impl, will leave for now
- mFailures = 1;
- mUptimeStartMs = now;
- } else {
- mFailures++;
+ mFailureHistory.addLast(now);
+ while (now - mFailureHistory.peekFirst() > mTriggerFailureDurationMs) {
+ // Prune values falling out of the window
+ mFailureHistory.removeFirst();
}
- boolean failed = mFailures >= mTriggerFailureCount;
+ boolean failed = mFailureHistory.size() >= mTriggerFailureCount;
if (failed) {
- mFailures = 0;
+ mFailureHistory.clear();
}
return failed;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7f69a683b18a..146be5aa7044 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -882,20 +882,20 @@ public class AppOpsService extends IAppOpsService.Stub {
final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
final String[] changedPkgs = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
- ArraySet<ModeCallback> callbacks;
- synchronized (AppOpsService.this) {
- callbacks = mOpModeWatchers.get(OP_PLAY_AUDIO);
- if (callbacks == null) {
- return;
+ for (int code : OPS_RESTRICTED_ON_SUSPEND) {
+ ArraySet<ModeCallback> callbacks;
+ synchronized (AppOpsService.this) {
+ callbacks = mOpModeWatchers.get(code);
+ if (callbacks == null) {
+ continue;
+ }
+ callbacks = new ArraySet<>(callbacks);
}
- callbacks = new ArraySet<>(callbacks);
- }
- for (int i = 0; i < changedUids.length; i++) {
- final int changedUid = changedUids[i];
- final String changedPkg = changedPkgs[i];
- // We trust packagemanager to insert matching uid and packageNames in the
- // extras
- for (int code : OPS_RESTRICTED_ON_SUSPEND) {
+ for (int i = 0; i < changedUids.length; i++) {
+ final int changedUid = changedUids[i];
+ final String changedPkg = changedPkgs[i];
+ // We trust packagemanager to insert matching uid and packageNames in the
+ // extras
notifyOpChanged(callbacks, code, changedUid, changedPkg);
}
}
@@ -2852,9 +2852,11 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) {
+ if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) {
+ return false;
+ }
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- return ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)
- && pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
+ return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
}
private boolean isOpRestrictedLocked(int uid, int code, String packageName,
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
index b27d5ea30c67..f8ffb7c1c0e2 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
@@ -23,7 +23,6 @@ import android.content.ServiceConnection;
import android.media.tv.ITvRemoteProvider;
import android.media.tv.ITvRemoteServiceInput;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -49,7 +48,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
private final ComponentName mComponentName;
private final int mUserId;
private final int mUid;
- private final Handler mHandler;
/**
* State guarded by mLock.
@@ -65,15 +63,14 @@ final class TvRemoteProviderProxy implements ServiceConnection {
private boolean mRunning;
private boolean mBound;
private Connection mActiveConnection;
- private boolean mConnectionReady;
- public TvRemoteProviderProxy(Context context, ComponentName componentName, int userId,
- int uid) {
+ TvRemoteProviderProxy(Context context, ProviderMethods provider,
+ ComponentName componentName, int userId, int uid) {
mContext = context;
+ mProviderMethods = provider;
mComponentName = componentName;
mUserId = userId;
mUid = uid;
- mHandler = new Handler();
}
public void dump(PrintWriter pw, String prefix) {
@@ -82,11 +79,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
pw.println(prefix + " mRunning=" + mRunning);
pw.println(prefix + " mBound=" + mBound);
pw.println(prefix + " mActiveConnection=" + mActiveConnection);
- pw.println(prefix + " mConnectionReady=" + mConnectionReady);
- }
-
- public void setProviderSink(ProviderMethods provider) {
- mProviderMethods = provider;
}
public boolean hasComponentName(String packageName, String className) {
@@ -101,7 +93,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
mRunning = true;
- updateBinding();
+ bind();
}
}
@@ -112,31 +104,19 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
mRunning = false;
- updateBinding();
+ unbind();
}
}
public void rebindIfDisconnected() {
synchronized (mLock) {
- if (mActiveConnection == null && shouldBind()) {
+ if (mActiveConnection == null && mRunning) {
unbind();
bind();
}
}
}
- private void updateBinding() {
- if (shouldBind()) {
- bind();
- } else {
- unbind();
- }
- }
-
- private boolean shouldBind() {
- return mRunning;
- }
-
private void bind() {
if (!mBound) {
if (DEBUG) {
@@ -208,48 +188,19 @@ final class TvRemoteProviderProxy implements ServiceConnection {
disconnect();
}
-
- private void onConnectionReady(Connection connection) {
- synchronized (mLock) {
- if (DEBUG) Slog.d(TAG, "onConnectionReady");
- if (mActiveConnection == connection) {
- if (DEBUG) Slog.d(TAG, "mConnectionReady = true");
- mConnectionReady = true;
- }
- }
- }
-
- private void onConnectionDied(Connection connection) {
- if (mActiveConnection == connection) {
- if (DEBUG) Slog.d(TAG, this + ": Service connection died");
- disconnect();
- }
- }
-
private void disconnect() {
synchronized (mLock) {
if (mActiveConnection != null) {
- mConnectionReady = false;
mActiveConnection.dispose();
mActiveConnection = null;
}
}
}
- // Provider helpers
- public void inputBridgeConnected(IBinder token) {
- synchronized (mLock) {
- if (DEBUG) Slog.d(TAG, this + ": inputBridgeConnected token: " + token);
- if (mConnectionReady) {
- mActiveConnection.onInputBridgeConnected(token);
- }
- }
- }
-
- public interface ProviderMethods {
+ interface ProviderMethods {
// InputBridge
- void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
- int width, int height, int maxPointers);
+ boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
+ int width, int height, int maxPointers);
void closeInputBridge(TvRemoteProviderProxy provider, IBinder token);
@@ -267,7 +218,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
void sendPointerSync(TvRemoteProviderProxy provider, IBinder token);
}
- private final class Connection implements IBinder.DeathRecipient {
+ private final class Connection {
private final ITvRemoteProvider mTvRemoteProvider;
private final RemoteServiceInputProvider mServiceInputProvider;
@@ -279,24 +230,16 @@ final class TvRemoteProviderProxy implements ServiceConnection {
public boolean register() {
if (DEBUG) Slog.d(TAG, "Connection::register()");
try {
- mTvRemoteProvider.asBinder().linkToDeath(this, 0);
mTvRemoteProvider.setRemoteServiceInputSink(mServiceInputProvider);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onConnectionReady(Connection.this);
- }
- });
return true;
} catch (RemoteException ex) {
- binderDied();
+ dispose();
+ return false;
}
- return false;
}
public void dispose() {
if (DEBUG) Slog.d(TAG, "Connection::dispose()");
- mTvRemoteProvider.asBinder().unlinkToDeath(this, 0);
mServiceInputProvider.dispose();
}
@@ -310,16 +253,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
}
- @Override
- public void binderDied() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onConnectionDied(Connection.this);
- }
- });
- }
-
void openInputBridge(final IBinder token, final String name, final int width,
final int height, final int maxPointers) {
synchronized (mLock) {
@@ -330,9 +263,9 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token,
- name, width, height, maxPointers);
+ if (mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token,
+ name, width, height, maxPointers)) {
+ onInputBridgeConnected(token);
}
} finally {
Binder.restoreCallingIdentity(idToken);
@@ -356,9 +289,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token);
- }
+ mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -381,9 +312,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token);
- }
+ mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -412,10 +341,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token,
- keyCode);
- }
+ mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token, keyCode);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -438,9 +364,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode);
- }
+ mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -463,10 +387,8 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token,
- pointerId, x, y);
- }
+ mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token,
+ pointerId, x, y);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -489,10 +411,8 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token,
- pointerId);
- }
+ mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token,
+ pointerId);
} finally {
Binder.restoreCallingIdentity(idToken);
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
index d27970f4882c..0d29edd02663 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
@@ -45,7 +45,7 @@ final class TvRemoteProviderWatcher {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
private final Context mContext;
- private final ProviderMethods mProvider;
+ private final TvRemoteProviderProxy.ProviderMethods mProvider;
private final Handler mHandler;
private final PackageManager mPackageManager;
private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
@@ -54,10 +54,10 @@ final class TvRemoteProviderWatcher {
private boolean mRunning;
- public TvRemoteProviderWatcher(Context context, ProviderMethods provider, Handler handler) {
+ TvRemoteProviderWatcher(Context context, TvRemoteProviderProxy.ProviderMethods provider) {
mContext = context;
mProvider = provider;
- mHandler = handler;
+ mHandler = new Handler(true);
mUserId = UserHandle.myUserId();
mPackageManager = context.getPackageManager();
mUnbundledServicePackage = context.getString(
@@ -116,12 +116,11 @@ final class TvRemoteProviderWatcher {
int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
if (sourceIndex < 0) {
TvRemoteProviderProxy providerProxy =
- new TvRemoteProviderProxy(mContext,
+ new TvRemoteProviderProxy(mContext, mProvider,
new ComponentName(serviceInfo.packageName, serviceInfo.name),
mUserId, serviceInfo.applicationInfo.uid);
providerProxy.start();
mProviderProxies.add(targetIndex++, providerProxy);
- mProvider.addProvider(providerProxy);
} else if (sourceIndex >= targetIndex) {
TvRemoteProviderProxy provider = mProviderProxies.get(sourceIndex);
provider.start(); // restart the provider if needed
@@ -135,7 +134,6 @@ final class TvRemoteProviderWatcher {
if (targetIndex < mProviderProxies.size()) {
for (int i = mProviderProxies.size() - 1; i >= targetIndex; i--) {
TvRemoteProviderProxy providerProxy = mProviderProxies.get(i);
- mProvider.removeProvider(providerProxy);
mProviderProxies.remove(providerProxy);
providerProxy.stop();
}
@@ -212,10 +210,4 @@ final class TvRemoteProviderWatcher {
scanPackages();
}
};
-
- public interface ProviderMethods {
- void addProvider(TvRemoteProviderProxy providerProxy);
-
- void removeProvider(TvRemoteProviderProxy providerProxy);
- }
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteService.java b/services/core/java/com/android/server/tv/TvRemoteService.java
index ba74791842d1..bee6fb34a899 100644
--- a/services/core/java/com/android/server/tv/TvRemoteService.java
+++ b/services/core/java/com/android/server/tv/TvRemoteService.java
@@ -17,11 +17,8 @@
package com.android.server.tv;
import android.content.Context;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.Looper;
-import android.os.Message;
import android.util.ArrayMap;
import android.util.Slog;
@@ -29,7 +26,6 @@ import com.android.server.SystemService;
import com.android.server.Watchdog;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Map;
/**
@@ -44,9 +40,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
private static final boolean DEBUG = false;
private static final boolean DEBUG_KEYS = false;
+ private final TvRemoteProviderWatcher mWatcher;
private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap();
- private Map<IBinder, TvRemoteProviderProxy> mProviderMap = new ArrayMap();
- private ArrayList<TvRemoteProviderProxy> mProviderList = new ArrayList<>();
/**
* State guarded by mLock.
@@ -60,11 +55,10 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
*/
private final Object mLock = new Object();
- public final UserHandler mHandler;
-
public TvRemoteService(Context context) {
super(context);
- mHandler = new UserHandler(new UserProvider(TvRemoteService.this), context);
+ mWatcher = new TvRemoteProviderWatcher(context,
+ new UserProvider(TvRemoteService.this));
Watchdog.getInstance().addMonitor(this);
}
@@ -80,21 +74,17 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
@Override
public void onBootPhase(int phase) {
+ // All lifecycle methods are called from the system server's main looper thread.
if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
if (DEBUG) Slog.d(TAG, "PHASE_THIRD_PARTY_APPS_CAN_START");
- mHandler.sendEmptyMessage(UserHandler.MSG_START);
- }
- }
- //Outgoing calls.
- private void informInputBridgeConnected(IBinder token) {
- mHandler.obtainMessage(UserHandler.MSG_INPUT_BRIDGE_CONNECTED, 0, 0, token).sendToTarget();
+ mWatcher.start(); // Also schedules the start of all providers.
+ }
}
- // Incoming calls.
- private void openInputBridgeInternalLocked(TvRemoteProviderProxy provider, final IBinder token,
- String name, int width, int height,
- int maxPointers) {
+ private boolean openInputBridgeInternalLocked(final IBinder token,
+ String name, int width, int height,
+ int maxPointers) {
if (DEBUG) {
Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name +
", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers);
@@ -104,15 +94,11 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
//Create a new bridge, if one does not exist already
if (mBridgeMap.containsKey(token)) {
if (DEBUG) Slog.d(TAG, "RemoteBridge already exists");
- // Respond back with success.
- informInputBridgeConnected(token);
- return;
+ return true;
}
UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers);
-
mBridgeMap.put(token, inputBridge);
- mProviderMap.put(token, provider);
try {
token.linkToDeath(new IBinder.DeathRecipient() {
@@ -126,15 +112,13 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
} catch (RemoteException e) {
if (DEBUG) Slog.d(TAG, "Token is already dead");
closeInputBridgeInternalLocked(token);
- return;
+ return false;
}
-
- // Respond back with success.
- informInputBridgeConnected(token);
-
} catch (IOException ioe) {
Slog.e(TAG, "Cannot create device for " + name);
+ return false;
}
+ return true;
}
private void closeInputBridgeInternalLocked(IBinder token) {
@@ -149,7 +133,6 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
mBridgeMap.remove(token);
- mProviderMap.remove(token);
}
private void clearInputBridgeInternalLocked(IBinder token) {
@@ -220,47 +203,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
}
- private final class UserHandler extends Handler {
-
- public static final int MSG_START = 1;
- public static final int MSG_INPUT_BRIDGE_CONNECTED = 2;
-
- private final TvRemoteProviderWatcher mWatcher;
- private boolean mRunning;
-
- public UserHandler(UserProvider provider, Context context) {
- super(Looper.getMainLooper(), null, true);
- mWatcher = new TvRemoteProviderWatcher(context, provider, this);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START: {
- start();
- break;
- }
- case MSG_INPUT_BRIDGE_CONNECTED: {
- IBinder token = (IBinder) msg.obj;
- TvRemoteProviderProxy provider = mProviderMap.get(token);
- if (provider != null) {
- provider.inputBridgeConnected(token);
- }
- break;
- }
- }
- }
-
- private void start() {
- if (!mRunning) {
- mRunning = true;
- mWatcher.start(); // also starts all providers
- }
- }
- }
-
- private final class UserProvider implements TvRemoteProviderWatcher.ProviderMethods,
- TvRemoteProviderProxy.ProviderMethods {
+ private final class UserProvider implements TvRemoteProviderProxy.ProviderMethods {
private final TvRemoteService mService;
@@ -269,8 +212,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
@Override
- public void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
- int width, int height, int maxPointers) {
+ public boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
+ int width, int height, int maxPointers) {
if (DEBUG) {
Slog.d(TAG, "openInputBridge(), token: " + token +
", name: " + name + ", width: " + width +
@@ -278,10 +221,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
- mService.openInputBridgeInternalLocked(provider, token, name, width, height,
- maxPointers);
- }
+ return mService.openInputBridgeInternalLocked(token, name, width,
+ height, maxPointers);
}
}
@@ -289,9 +230,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token);
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.closeInputBridgeInternalLocked(token);
- }
}
}
@@ -299,9 +238,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token);
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.clearInputBridgeInternalLocked(token);
- }
}
}
@@ -311,9 +248,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendKeyDownInternalLocked(token, keyCode);
- }
}
}
@@ -323,9 +258,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendKeyUpInternalLocked(token, keyCode);
- }
}
}
@@ -336,9 +269,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendPointerDownInternalLocked(token, pointerId, x, y);
- }
}
}
@@ -348,9 +279,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendPointerUpInternalLocked(token, pointerId);
- }
}
}
@@ -358,29 +287,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token);
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendPointerSyncInternalLocked(token);
- }
- }
- }
-
- @Override
- public void addProvider(TvRemoteProviderProxy provider) {
- if (DEBUG) Slog.d(TAG, "addProvider " + provider);
- synchronized (mLock) {
- provider.setProviderSink(this);
- mProviderList.add(provider);
- Slog.d(TAG, "provider: " + provider.toString());
- }
- }
-
- @Override
- public void removeProvider(TvRemoteProviderProxy provider) {
- if (DEBUG) Slog.d(TAG, "removeProvider " + provider);
- synchronized (mLock) {
- if (mProviderList.remove(provider) == false) {
- Slog.e(TAG, "Unknown provider " + provider);
- }
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b0f1e5d69be4..3cdb59beb23c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -37,6 +37,7 @@ import android.app.UserSwitchObserver;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.app.WallpaperManager.SetWallpaperFlags;
import android.app.admin.DevicePolicyManager;
import android.app.backup.WallpaperBackupHelper;
import android.content.BroadcastReceiver;
@@ -73,7 +74,6 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SELinux;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -91,9 +91,9 @@ import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.IWindowManager;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
@@ -739,7 +739,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
private final Context mContext;
- private final IWindowManager mIWindowManager;
private final WindowManagerInternal mWindowManagerInternal;
private final IPackageManager mIPackageManager;
private final MyPackageMonitor mMonitor;
@@ -792,7 +791,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
*/
private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
mColorsChangedListeners;
- private WallpaperData mLastWallpaper;
+ protected WallpaperData mLastWallpaper;
private IWallpaperManagerCallback mKeyguardListener;
private boolean mWaitingForUnlock;
private boolean mShuttingDown;
@@ -825,7 +824,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
- private WallpaperData mFallbackWallpaper;
+ protected WallpaperData mFallbackWallpaper;
private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -900,9 +899,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
- WallpaperData(int userId, String inputFileName, String cropFileName) {
+ WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) {
this.userId = userId;
- final File wallpaperDir = getWallpaperDir(userId);
wallpaperFile = new File(wallpaperDir, inputFileName);
cropFile = new File(wallpaperDir, cropFileName);
}
@@ -917,7 +915,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private static final class DisplayData {
+ @VisibleForTesting
+ static final class DisplayData {
int mWidth = -1;
int mHeight = -1;
final Rect mPadding = new Rect(0, 0, 0, 0);
@@ -1057,13 +1056,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
- try {
- mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
- return;
- }
-
+ mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
@@ -1081,10 +1074,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
void disconnectLocked() {
if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
- try {
- mIWindowManager.removeWindowToken(mToken, mDisplayId);
- } catch (RemoteException e) {
- }
+ mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */,
+ mDisplayId);
try {
if (mEngine != null) {
mEngine.destroy();
@@ -1562,6 +1553,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ @VisibleForTesting
+ WallpaperData getCurrentWallpaperData(@SetWallpaperFlags int which, int userId) {
+ synchronized (mLock) {
+ final SparseArray<WallpaperData> wallpaperDataMap =
+ which == FLAG_SYSTEM ? mWallpaperMap : mLockWallpaperMap;
+ return wallpaperDataMap.get(userId);
+ }
+ }
+
public WallpaperManagerService(Context context) {
if (DEBUG) Slog.v(TAG, "WallpaperService startup");
mContext = context;
@@ -1569,8 +1569,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
- mIWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService(Context.WINDOW_SERVICE));
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mIPackageManager = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1600,7 +1598,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
}
- private static File getWallpaperDir(int userId) {
+ File getWallpaperDir(int userId) {
return Environment.getUserSystemDirectory(userId);
}
@@ -1819,7 +1817,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// while locked, so pretend like the component was actually
// bound into place
wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
- final WallpaperData fallback = new WallpaperData(wallpaper.userId,
+ final WallpaperData fallback =
+ new WallpaperData(wallpaper.userId, getWallpaperDir(wallpaper.userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
@@ -2380,7 +2379,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// We know a-priori that there is no lock-only wallpaper currently
- WallpaperData lockWP = new WallpaperData(userId,
+ WallpaperData lockWP = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
@@ -2793,7 +2792,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private static JournaledFile makeJournaledFile(int userId) {
+ private JournaledFile makeJournaledFile(int userId) {
final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -2958,7 +2957,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// it now.
if (wallpaper == null) {
if (which == FLAG_LOCK) {
- wallpaper = new WallpaperData(userId,
+ wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
@@ -2966,7 +2965,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// sanity fallback: we're in bad shape, but establishing a known
// valid system+lock WallpaperData will keep us from dying.
Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
- wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
+ wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
+ WALLPAPER, WALLPAPER_CROP);
mWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
}
@@ -2985,7 +2985,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// Do this once per boot
migrateFromOld();
- wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
+ wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
+ WALLPAPER, WALLPAPER_CROP);
wallpaper.allowBackup = true;
mWallpaperMap.put(userId, wallpaper);
if (!wallpaper.cropExists()) {
@@ -3037,7 +3038,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// keyguard-specific wallpaper for this user
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper == null) {
- lockWallpaper = new WallpaperData(userId,
+ lockWallpaper = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, lockWallpaper);
}
@@ -3088,8 +3089,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private void initializeFallbackWallpaper() {
if (mFallbackWallpaper == null) {
if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
- mFallbackWallpaper = new WallpaperData(
- UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
+ final int systemUserId = UserHandle.USER_SYSTEM;
+ mFallbackWallpaper = new WallpaperData(systemUserId, getWallpaperDir(systemUserId),
+ WALLPAPER, WALLPAPER_CROP);
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 55db1a06664e..b35bd9e4e81a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1535,6 +1535,11 @@ class ActivityStarter {
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTaskRecord() : null;
setNewTask(taskToAffiliate);
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ mStartActivity.getTaskRecord())) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+ return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
} else if (mAddingToTask) {
addOrReparentStartingActivity(targetTask, "adding to task");
}
@@ -1654,9 +1659,8 @@ class ActivityStarter {
final boolean isNewClearTask =
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- if (mService.getLockTaskController().isInLockTaskMode() && (newTask
- || mService.getLockTaskController().isLockTaskModeViolation(targetTask,
- isNewClearTask))) {
+ if (!newTask && mService.getLockTaskController().isLockTaskModeViolation(targetTask,
+ isNewClearTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -2538,7 +2542,8 @@ class ActivityStarter {
final boolean onTop =
(aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
final ActivityStack stack =
- mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams);
+ mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
+ mRequest.realCallingPid, mRequest.realCallingUid);
return stack;
}
// Otherwise handle adjacent launch.
@@ -2656,11 +2661,24 @@ class ActivityStarter {
return this;
}
+ /**
+ * Sets the pid of the caller who originally started the activity.
+ *
+ * Normally, the pid/uid would be the calling pid from the binder call.
+ * However, in case of a {@link PendingIntent}, the pid/uid pair of the caller is considered
+ * the original entity that created the pending intent, in contrast to setRealCallingPid/Uid,
+ * which represents the entity who invoked pending intent via {@link PendingIntent#send}.
+ */
ActivityStarter setCallingPid(int pid) {
mRequest.callingPid = pid;
return this;
}
+ /**
+ * Sets the uid of the caller who originally started the activity.
+ *
+ * @see #setCallingPid
+ */
ActivityStarter setCallingUid(int uid) {
mRequest.callingUid = uid;
return this;
@@ -2671,11 +2689,25 @@ class ActivityStarter {
return this;
}
+ /**
+ * Sets the pid of the caller who requested to launch the activity.
+ *
+ * The pid/uid represents the caller who launches the activity in this request.
+ * It will almost same as setCallingPid/Uid except when processing {@link PendingIntent}:
+ * the pid/uid will be the caller who called {@link PendingIntent#send()}.
+ *
+ * @see #setCallingPid
+ */
ActivityStarter setRealCallingPid(int pid) {
mRequest.realCallingPid = pid;
return this;
}
+ /**
+ * Sets the uid of the caller who requested to launch the activity.
+ *
+ * @see #setRealCallingPid
+ */
ActivityStarter setRealCallingUid(int uid) {
mRequest.realCallingUid = uid;
return this;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index caf87cd6a906..b30da5e156e2 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -280,13 +280,6 @@ public class LockTaskController {
}
/**
- * @return true if currently in the lock task mode, otherwise, return false.
- */
- boolean isInLockTaskMode() {
- return !mLockTaskModeTasks.isEmpty();
- }
-
- /**
* @return whether the requested task is disallowed to be launched.
*/
boolean isLockTaskModeViolation(TaskRecord task) {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d9e30a2da9a5..eb6b51e3388b 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1615,7 +1615,8 @@ class RootActivityContainer extends ConfigurationContainer
<T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
- return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
+ return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */,
+ -1 /* no realCallingPid */, -1 /* no realCallingUid */);
}
/**
@@ -1624,13 +1625,16 @@ class RootActivityContainer extends ConfigurationContainer
* @param r The activity we are trying to launch. Can be null.
* @param options The activity options used to the launch. Can be null.
* @param candidateTask The possible task the activity might be launched in. Can be null.
- * @params launchParams The resolved launch params to use.
+ * @param launchParams The resolved launch params to use.
+ * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
+ * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
*
* @return The stack to use for the launch or INVALID_STACK_ID.
*/
<T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
+ @Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid,
+ int realCallingUid) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
//Rect bounds = null;
@@ -1661,7 +1665,15 @@ class RootActivityContainer extends ConfigurationContainer
if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
displayId = launchParams.mPreferredDisplayId;
}
- if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+ final boolean canLaunchOnDisplayFromStartRequest =
+ realCallingPid != 0 && realCallingUid > 0 && r != null
+ && mStackSupervisor.canPlaceEntityOnDisplay(displayId, realCallingPid,
+ realCallingUid, r.info);
+ // Checking if the activity's launch caller, or the realCallerId of the activity from
+ // start request (i.e. entity that invokes PendingIntent) is allowed to launch on the
+ // display.
+ if (displayId != INVALID_DISPLAY && (canLaunchOnDisplay(r, displayId)
+ || canLaunchOnDisplayFromStartRequest)) {
if (r != null) {
stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
launchParams);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7ad8b58d1c7d..607a013abc51 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1216,7 +1216,8 @@ public class WindowManagerService extends IWindowManager.Stub
mPropertiesChangedListener = properties -> {
synchronized (mGlobalLock) {
final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
- properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 478bc88fe815..3154c7021255 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1999,6 +1999,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return LocalServices.getService(LockSettingsInternal.class);
}
+ boolean hasUserSetupCompleted(DevicePolicyData userData) {
+ return userData.mUserSetupComplete;
+ }
+
boolean isBuildDebuggable() {
return Build.IS_DEBUGGABLE;
}
@@ -8271,7 +8275,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
- return getUserData(userHandle).mUserSetupComplete;
+ return mInjector.hasUserSetupCompleted(getUserData(userHandle));
}
private boolean hasPaired(int userHandle) {
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index ad94e6159b87..8699669bf4a5 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -25,6 +25,7 @@ android_test {
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"truth-prebuilt",
+ "testables",
],
libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
new file mode 100644
index 000000000000..307092d24d84
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.reset;
+
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.WallpaperManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.service.wallpaper.IWallpaperConnection;
+import android.service.wallpaper.WallpaperService;
+import android.testing.TestableContext;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.wallpaper.WallpaperManagerService.WallpaperData;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Tests for the {@link WallpaperManagerService} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:WallpaperManagerServiceTests
+ */
+@Presubmit
+@FlakyTest(bugId = 129797242)
+@RunWith(AndroidJUnit4.class)
+public class WallpaperManagerServiceTests {
+ private static final int DISPLAY_SIZE_DIMENSION = 100;
+ private static StaticMockitoSession sMockitoSession;
+
+ @ClassRule
+ public static final TestableContext sContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ private static ComponentName sImageWallpaperComponentName;
+ private static ComponentName sDefaultWallpaperComponent;
+
+ private IPackageManager mIpm = AppGlobals.getPackageManager();
+
+ @Mock
+ private DisplayManager mDisplayManager;
+
+ @Rule
+ public final TemporaryFolder mFolder = new TemporaryFolder();
+ private final SparseArray<File> mTempDirs = new SparseArray<>();
+ private WallpaperManagerService mService;
+
+ @BeforeClass
+ public static void setUpClass() {
+ sMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(LocalServices.class)
+ .spyStatic(WallpaperManager.class)
+ .startMocking();
+
+ final WindowManagerInternal dmi = mock(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, dmi);
+
+ sContext.addMockSystemService(Context.APP_OPS_SERVICE, mock(AppOpsManager.class));
+
+ spyOn(sContext);
+ sContext.getTestablePermissions().setPermission(
+ android.Manifest.permission.SET_WALLPAPER_COMPONENT,
+ PackageManager.PERMISSION_GRANTED);
+ sContext.getTestablePermissions().setPermission(
+ android.Manifest.permission.SET_WALLPAPER,
+ PackageManager.PERMISSION_GRANTED);
+ doNothing().when(sContext).sendBroadcastAsUser(any(), any());
+
+ //Wallpaper components
+ final IWallpaperConnection.Stub wallpaperService = mock(IWallpaperConnection.Stub.class);
+ sImageWallpaperComponentName = ComponentName.unflattenFromString(
+ sContext.getResources().getString(R.string.image_wallpaper_component));
+ // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper.
+ sDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(sContext);
+
+ if (sDefaultWallpaperComponent == null) {
+ sDefaultWallpaperComponent = sImageWallpaperComponentName;
+ doReturn(sImageWallpaperComponentName).when(() ->
+ WallpaperManager.getDefaultWallpaperComponent(any()));
+ } else {
+ sContext.addMockService(sDefaultWallpaperComponent, wallpaperService);
+ }
+
+ sContext.addMockService(sImageWallpaperComponentName, wallpaperService);
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ if (sMockitoSession != null) {
+ sMockitoSession.finishMocking();
+ sMockitoSession = null;
+ }
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ sImageWallpaperComponentName = null;
+ sDefaultWallpaperComponent = null;
+ reset(sContext);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ sContext.addMockSystemService(DisplayManager.class, mDisplayManager);
+
+ final Display mockDisplay = mock(Display.class);
+ doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension();
+ doReturn(mockDisplay).when(mDisplayManager).getDisplay(anyInt());
+
+ final Display[] displays = new Display[]{mockDisplay};
+ doReturn(displays).when(mDisplayManager).getDisplays();
+
+ spyOn(mIpm);
+ mService = new TestWallpaperManagerService(sContext);
+ spyOn(mService);
+ mService.systemReady();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WallpaperManagerInternal.class);
+
+ mTempDirs.clear();
+ reset(mIpm);
+ mService = null;
+ }
+
+ protected class TestWallpaperManagerService extends WallpaperManagerService {
+ private static final String TAG = "TestWallpaperManagerService";
+
+ TestWallpaperManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ File getWallpaperDir(int userId) {
+ File tempDir = mTempDirs.get(userId);
+ if (tempDir == null) {
+ try {
+ tempDir = mFolder.newFolder(String.valueOf(userId));
+ mTempDirs.append(userId, tempDir);
+ } catch (IOException e) {
+ Log.e(TAG, "getWallpaperDir failed at userId= " + userId);
+ }
+ }
+ return tempDir;
+ }
+
+ // Always return true for test
+ @Override
+ public boolean isWallpaperSupported(String callingPackage) {
+ return true;
+ }
+
+ // Always return true for test
+ @Override
+ public boolean isSetWallpaperAllowed(String callingPackage) {
+ return true;
+ }
+ }
+
+ /**
+ * Tests that internal basic data should be correct after boot up.
+ */
+ @Test
+ public void testDataCorrectAfterBoot() {
+ mService.switchUser(UserHandle.USER_SYSTEM, null);
+
+ final WallpaperData fallbackData = mService.mFallbackWallpaper;
+ assertEquals("Fallback wallpaper component should be ImageWallpaper.",
+ sImageWallpaperComponentName, fallbackData.wallpaperComponent);
+
+ verifyLastWallpaperData(UserHandle.USER_SYSTEM, sDefaultWallpaperComponent);
+ verifyDisplayData();
+ }
+
+ /**
+ * Tests setWallpaperComponent and clearWallpaper should work as expected.
+ */
+ @Test
+ public void testSetThenClearComponent() {
+ // Skip if there is no pre-defined default wallpaper component.
+ assumeThat(sDefaultWallpaperComponent,
+ not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
+
+ final int testUserId = UserHandle.USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(testUserId);
+
+ mService.setWallpaperComponent(sImageWallpaperComponentName);
+ verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyCurrentSystemData(testUserId);
+
+ mService.clearWallpaper(null, FLAG_SYSTEM, testUserId);
+ verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(testUserId);
+ }
+
+ /**
+ * Tests internal data should be correct and no crash after switch user continuously.
+ */
+ @Test
+ public void testSwitchMultipleUsers() throws Exception {
+ final int lastUserId = 5;
+ final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
+ doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt());
+
+ final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ final ParceledListSlice ris =
+ mIpm.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(sContext.getContentResolver()),
+ PackageManager.GET_META_DATA, 0);
+ doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
+ eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
+
+ for (int userId = 0; userId <= lastUserId; userId++) {
+ mService.switchUser(userId, null);
+ verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(userId);
+ }
+ verifyNoConnectionBeforeLastUser(lastUserId);
+ }
+
+ /**
+ * Tests internal data should be correct and no crash after switch user + unlock user
+ * continuously.
+ * Simulating that the selected WallpaperService is not built-in. After switching users, the
+ * service should not be bound, but bound to the image wallpaper. After receiving the user
+ * unlock callback and can find the selected service for the user, the selected service should
+ * be bound.
+ */
+ @Test
+ public void testSwitchThenUnlockMultipleUsers() throws Exception {
+ final int lastUserId = 5;
+ final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
+ doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt());
+
+ final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ final ParceledListSlice ris =
+ mIpm.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(sContext.getContentResolver()),
+ PackageManager.GET_META_DATA, 0);
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
+ eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
+
+ for (int userId = 1; userId <= lastUserId; userId++) {
+ mService.switchUser(userId, null);
+ verifyLastWallpaperData(userId, sImageWallpaperComponentName);
+ // Simulate user unlocked
+ doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), eq(userId));
+ mService.onUnlockUser(userId);
+ verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(userId);
+ }
+ verifyNoConnectionBeforeLastUser(lastUserId);
+ verifyDisplayData();
+ }
+
+ // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
+ // non-current user must not bind to wallpaper service.
+ private void verifyNoConnectionBeforeLastUser(int lastUserId) {
+ for (int i = 0; i < lastUserId; i++) {
+ final WallpaperData userData = mService.getCurrentWallpaperData(FLAG_SYSTEM, i);
+ assertNull("No user data connection left", userData.connection);
+ }
+ }
+
+ private void verifyLastWallpaperData(int lastUserId, ComponentName expectedComponent) {
+ final WallpaperData lastData = mService.mLastWallpaper;
+ assertNotNull("Last wallpaper must not be null", lastData);
+ assertEquals("Last wallpaper component must be equals.", expectedComponent,
+ lastData.wallpaperComponent);
+ assertEquals("The user id in last wallpaper should be the last switched user",
+ lastUserId, lastData.userId);
+ assertNotNull("Must exist user data connection on last wallpaper data",
+ lastData.connection);
+ }
+
+ private void verifyCurrentSystemData(int userId) {
+ final WallpaperData lastData = mService.mLastWallpaper;
+ final WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, userId);
+ assertEquals("Last wallpaper should be equals to current system wallpaper",
+ lastData, wallpaper);
+ }
+
+ private void verifyDisplayData() {
+ mService.forEachDisplayData(data -> {
+ assertTrue("Display width must larger than maximum screen size",
+ data.mWidth >= DISPLAY_SIZE_DIMENSION);
+ assertTrue("Display height must larger than maximum screen size",
+ data.mHeight >= DISPLAY_SIZE_DIMENSION);
+ });
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
index bd3d9ab2220d..3852b9fec001 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
@@ -17,6 +17,7 @@ package com.android.server.pm;
import android.content.Context;
import android.content.pm.ModuleInfo;
+import android.content.pm.PackageManager;
import android.test.InstrumentationTestCase;
import com.android.frameworks.servicestests.R;
@@ -28,7 +29,7 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase {
public void testSuccessfulParse() {
ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
- List<ModuleInfo> mi = provider.getInstalledModules(0);
+ List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL);
assertEquals(2, mi.size());
Collections.sort(mi, (ModuleInfo m1, ModuleInfo m2) ->
@@ -49,18 +50,18 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase {
public void testParseFailure_incorrectTopLevelElement() {
ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata1);
- assertEquals(0, provider.getInstalledModules(0).size());
+ assertEquals(0, provider.getInstalledModules(PackageManager.MATCH_ALL).size());
}
public void testParseFailure_incorrectModuleElement() {
ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata2);
- assertEquals(0, provider.getInstalledModules(0).size());
+ assertEquals(0, provider.getInstalledModules(PackageManager.MATCH_ALL).size());
}
public void testParse_unknownAttributesIgnored() {
ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
- List<ModuleInfo> mi = provider.getInstalledModules(0);
+ List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL);
assertEquals(2, mi.size());
ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index 5ae043402526..2290ef79da78 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -18,7 +18,10 @@ package com.android.server.pm;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.opToName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -39,7 +42,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.res.Resources;
-import android.media.AudioAttributes;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.Handler;
@@ -553,28 +555,42 @@ public class SuspendPackagesTest {
}
@Test
- public void testAudioOpBlockedOnSuspend() throws Exception {
+ public void testCameraBlockedOnSuspend() throws Exception {
+ assertOpBlockedOnSuspend(OP_CAMERA);
+ }
+
+ @Test
+ public void testPlayAudioBlockedOnSuspend() throws Exception {
+ assertOpBlockedOnSuspend(OP_PLAY_AUDIO);
+ }
+
+ @Test
+ public void testRecordAudioBlockedOnSuspend() throws Exception {
+ assertOpBlockedOnSuspend(OP_RECORD_AUDIO);
+ }
+
+ private void assertOpBlockedOnSuspend(int code) throws Exception {
final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
final CountDownLatch latch = new CountDownLatch(1);
final IAppOpsCallback watcher = new IAppOpsCallback.Stub() {
@Override
public void opChanged(int op, int uid, String packageName) {
- if (op == OP_PLAY_AUDIO && packageName.equals(TEST_APP_PACKAGE_NAME)) {
+ if (op == code && packageName.equals(TEST_APP_PACKAGE_NAME)) {
latch.countDown();
}
}
};
- iAppOps.startWatchingMode(OP_PLAY_AUDIO, TEST_APP_PACKAGE_NAME, watcher);
+ iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher);
final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
- int audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
- AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
- assertEquals("Audio muted for unsuspended package", MODE_ALLOWED, audioOpMode);
+ int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
+ assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED,
+ opMode);
suspendTestPackage(null, null, null);
assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
- audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
- AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
- assertEquals("Audio not muted for suspended package", MODE_IGNORED, audioOpMode);
+ opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
+ assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED,
+ opMode);
iAppOps.stopWatchingMode(watcher);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 34cc0c742005..8393ae0c3aec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -354,7 +354,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean());
doReturn(stack).when(mRootActivityContainer)
- .getLaunchStack(any(), any(), any(), anyBoolean(), any());
+ .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
}
// Set up mock package manager internal and make sure no unmocked methods are called
@@ -501,7 +501,6 @@ public class ActivityStarterTests extends ActivityTestsBase {
final ActivityStarter starter = prepareStarter(0);
final LockTaskController lockTaskController = mService.getLockTaskController();
- doReturn(true).when(lockTaskController).isInLockTaskMode();
doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
final int result = starter.setReason("testTaskModeViolation").execute();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 1731f7cdd59c..d311dfc60675 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -139,6 +139,8 @@ class ActivityTestsBase {
private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
private boolean mLaunchTaskBehind;
private int mConfigChanges;
+ private int mLaunchedFromPid;
+ private int mLaunchedFromUid;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -214,6 +216,16 @@ class ActivityTestsBase {
return this;
}
+ ActivityBuilder setLaunchedFromPid(int pid) {
+ mLaunchedFromPid = pid;
+ return this;
+ }
+
+ ActivityBuilder setLaunchedFromUid(int uid) {
+ mLaunchedFromUid = uid;
+ return this;
+ }
+
ActivityRecord build() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
@@ -250,10 +262,11 @@ class ActivityTestsBase {
}
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
- 0 /* launchedFromPid */, 0, null, intent, null,
- aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
- 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
- mService.mStackSupervisor, options, null /* sourceRecord */);
+ mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */,
+ null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */,
+ null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/,
+ false /* rootVoiceInteraction */, mService.mStackSupervisor, options,
+ null /* sourceRecord */);
spyOn(activity);
if (mTaskRecord != null) {
// fullscreen value is normally read from resources in ctor, so for testing we need
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index c67b860b656e..aa97de72e507 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -61,6 +62,7 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
+import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -820,6 +822,41 @@ public class RootActivityContainerTests extends ActivityTestsBase {
}
/**
+ * Test that {@link RootActivityContainer#getLaunchStack} with the real caller id will get the
+ * expected stack when requesting the activity launch on the secondary display.
+ */
+ @Test
+ public void testGetLaunchStackWithRealCallerId() {
+ // Create a non-system owned virtual display.
+ final DisplayInfo info = new DisplayInfo();
+ mSupervisor.mService.mContext.getDisplay().getDisplayInfo(info);
+ info.type = TYPE_VIRTUAL;
+ info.ownerUid = 100;
+ final TestActivityDisplay secondaryDisplay = TestActivityDisplay.create(mSupervisor, info);
+ mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
+
+ // Create an activity with specify the original launch pid / uid.
+ final ActivityRecord r = new ActivityBuilder(mService).setLaunchedFromPid(200)
+ .setLaunchedFromUid(200).build();
+
+ // Simulate ActivityStarter to find a launch stack for requesting the activity to launch
+ // on the secondary display with realCallerId.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
+ 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
+ final ActivityStack result = mRootActivityContainer.getLaunchStack(r, options,
+ null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
+ 300 /* test realCallerUid */);
+
+ // Assert that the stack is returned as expected.
+ assertNotNull(result);
+ assertEquals("The display ID of the stack should same as secondary display ",
+ secondaryDisplay.mDisplayId, result.mDisplayId);
+ }
+
+ /**
* Mock {@link RootActivityContainer#resolveHomeActivity} for returning consistent activity
* info for test cases (the original implementation will resolve from the real package manager).
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8eeaf8d8f012..b4495787bb80 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -108,6 +108,19 @@ public class CarrierConfigManager {
"call_forwarding_visibility_bool";
/**
+ * Boolean indicating if carrier supports call forwarding option "When unreachable".
+ *
+ * {@code true}: Call forwarding option "When unreachable" is supported.
+ * {@code false}: Call forwarding option "When unreachable" is not supported. Option will be
+ * greyed out in the UI.
+ *
+ * By default this value is true.
+ * @hide
+ */
+ public static final String KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL =
+ "call_forwarding_when_unreachable_supported_bool";
+
+ /**
* Boolean indicating if the "Caller ID" item is visible in the Additional Settings menu.
* true means visible. false means gone.
* @hide
@@ -3250,6 +3263,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true);
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
+ sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index ab31ed7389a3..79f5095010e8 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -765,6 +765,78 @@ public class PackageWatchdogTest {
assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_B);
}
+ /**
+ * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
+ * an invalid durationMs.
+ */
+ @Test
+ public void testInvalidMonitoringDuration_beforeExpiry() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ // Note: Don't move too close to the expiration time otherwise the handler will be thrashed
+ // by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very
+ // small timeouts.
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS - 100);
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+
+ // We should receive APP_A since the observer hasn't expired
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
+ }
+
+ /**
+ * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
+ * an invalid durationMs.
+ */
+ @Test
+ public void testInvalidMonitoringDuration_afterExpiry() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1);
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+
+ // We should receive nothing since the observer has expired
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
+ }
+
+ /** Test we are notified when enough failures are triggered within any window. */
+ @Test
+ public void testFailureTriggerWindow() {
+ adoptShellPermissions(
+ Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(3), /*makeDefault*/false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
+ Integer.toString(1000), /*makeDefault*/false);
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
+ // Raise 2 failures at t=0 and t=900 respectively
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+ moveTimeForwardAndDispatch(900);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+
+ // Raise 2 failures at t=1100
+ moveTimeForwardAndDispatch(200);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+
+ // We should receive APP_A since there are 3 failures within 1000ms window
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
+ }
+
private void adoptShellPermissions(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 5e06818d7a13..e36668e5a043 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -53,9 +53,9 @@ using ::android::ConfigDescription;
using ::android::ResTable_config;
using ::android::StringPiece;
using ::android::base::ReadFileToString;
-using ::android::base::WriteStringToFile;
using ::android::base::StringAppendF;
using ::android::base::StringPrintf;
+using ::android::base::WriteStringToFile;
namespace aapt {
@@ -300,29 +300,7 @@ class Optimizer {
OptimizeContext* context_;
};
-bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context,
- OptimizeOptions* options) {
- std::string contents;
- if (!ReadFileToString(path, &contents, true)) {
- context->GetDiagnostics()->Error(DiagMessage()
- << "failed to parse whitelist from config file: " << path);
- return false;
- }
- for (StringPiece resource_name : util::Tokenize(contents, ',')) {
- options->table_flattener_options.whitelisted_resources.insert(
- resource_name.to_string());
- }
- return true;
-}
-
-bool ExtractConfig(const std::string& path, OptimizeContext* context,
- OptimizeOptions* options) {
- std::string content;
- if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
- context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist");
- return false;
- }
-
+bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOptions* options) {
size_t line_no = 0;
for (StringPiece line : util::Tokenize(content, '\n')) {
line_no++;
@@ -351,15 +329,24 @@ bool ExtractConfig(const std::string& path, OptimizeContext* context,
for (StringPiece directive : util::Tokenize(directives, ',')) {
if (directive == "remove") {
options->resources_blacklist.insert(resource_name.ToResourceName());
- } else if (directive == "no_obfuscate") {
- options->table_flattener_options.whitelisted_resources.insert(
- resource_name.entry.to_string());
+ } else if (directive == "no_collapse" || directive == "no_obfuscate") {
+ options->table_flattener_options.name_collapse_exemptions.insert(
+ resource_name.ToResourceName());
}
}
}
return true;
}
+bool ExtractConfig(const std::string& path, IAaptContext* context, OptimizeOptions* options) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading config file");
+ return false;
+ }
+ return ParseConfig(content, context, options);
+}
+
bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
OptimizeOptions* out_options) {
const xml::XmlResource* manifest = apk->GetManifest();
@@ -467,15 +454,6 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
}
}
- if (options_.table_flattener_options.collapse_key_stringpool) {
- if (whitelist_path_) {
- std::string& path = whitelist_path_.value();
- if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) {
- return 1;
- }
- }
- }
-
if (resources_config_path_) {
std::string& path = resources_config_path_.value();
if (!ExtractConfig(path, &context, &options_)) {
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 0be7dad18380..5070ccc8afbf 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -78,10 +78,6 @@ class OptimizeCommand : public Command {
"All the resources that would be unused on devices of the given densities will be \n"
"removed from the APK.",
&target_densities_);
- AddOptionalFlag("--whitelist-path",
- "Path to the whitelist.cfg file containing whitelisted resources \n"
- "whose names should not be altered in final resource tables.",
- &whitelist_path_);
AddOptionalFlag("--resources-config-path",
"Path to the resources.cfg file containing the list of resources and \n"
"directives to each resource. \n"
@@ -104,11 +100,13 @@ class OptimizeCommand : public Command {
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.",
&options_.table_flattener_options.use_sparse_entries);
- AddOptionalSwitch("--enable-resource-obfuscation",
- "Enables obfuscation of key string pool to single value",
+ AddOptionalSwitch("--collapse-resource-names",
+ "Collapses resource names to a single value in the key string pool. Resources can \n"
+ "be exempted using the \"no_collapse\" directive in a file specified by "
+ "--resources-config-path.",
&options_.table_flattener_options.collapse_key_stringpool);
- AddOptionalSwitch("--enable-resource-path-shortening",
- "Enables shortening of the path of the resources inside the APK.",
+ AddOptionalSwitch("--shorten-resource-paths",
+ "Shortens the paths of resources inside the APK.",
&options_.shorten_resource_paths);
AddOptionalFlag("--resource-path-shortening-map",
"Path to output the map of old resource paths to shortened paths.",
@@ -125,7 +123,6 @@ class OptimizeCommand : public Command {
const std::string &file_path);
Maybe<std::string> config_path_;
- Maybe<std::string> whitelist_path_;
Maybe<std::string> resources_config_path_;
Maybe<std::string> target_densities_;
std::vector<std::string> configs_;
diff --git a/tools/aapt2/cmd/Optimize_test.cpp b/tools/aapt2/cmd/Optimize_test.cpp
new file mode 100644
index 000000000000..ac681e85b3d6
--- /dev/null
+++ b/tools/aapt2/cmd/Optimize_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Optimize.h"
+
+#include "AppInfo.h"
+#include "Diagnostics.h"
+#include "LoadedApk.h"
+#include "Resource.h"
+#include "test/Test.h"
+
+using testing::Contains;
+using testing::Eq;
+
+namespace aapt {
+
+bool ParseConfig(const std::string&, IAaptContext*, OptimizeOptions*);
+
+using OptimizeTest = CommandTestFixture;
+
+TEST_F(OptimizeTest, ParseConfigWithNoCollapseExemptions) {
+ const std::string& content = R"(
+string/foo#no_collapse
+dimen/bar#no_collapse
+)";
+ aapt::test::Context context;
+ OptimizeOptions options;
+ ParseConfig(content, &context, &options);
+
+ const std::set<ResourceName>& name_collapse_exemptions =
+ options.table_flattener_options.name_collapse_exemptions;
+
+ ASSERT_THAT(name_collapse_exemptions.size(), Eq(2));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo")));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar")));
+}
+
+TEST_F(OptimizeTest, ParseConfigWithNoObfuscateExemptions) {
+ const std::string& content = R"(
+string/foo#no_obfuscate
+dimen/bar#no_obfuscate
+)";
+ aapt::test::Context context;
+ OptimizeOptions options;
+ ParseConfig(content, &context, &options);
+
+ const std::set<ResourceName>& name_collapse_exemptions =
+ options.table_flattener_options.name_collapse_exemptions;
+
+ ASSERT_THAT(name_collapse_exemptions.size(), Eq(2));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo")));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar")));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index b9321174100b..58e232c33985 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -228,14 +228,15 @@ class PackageFlattener {
public:
PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
- bool collapse_key_stringpool, const std::set<std::string>& whitelisted_resources)
+ bool collapse_key_stringpool,
+ const std::set<ResourceName>& name_collapse_exemptions)
: context_(context),
diag_(context->GetDiagnostics()),
package_(package),
shared_libs_(shared_libs),
use_sparse_entries_(use_sparse_entries),
collapse_key_stringpool_(collapse_key_stringpool),
- whitelisted_resources_(whitelisted_resources) {
+ name_collapse_exemptions_(name_collapse_exemptions) {
}
bool FlattenPackage(BigBuffer* buffer) {
@@ -652,11 +653,12 @@ class PackageFlattener {
for (ResourceEntry* entry : sorted_entries) {
uint32_t local_key_index;
+ ResourceName resource_name({}, type->type, entry->name);
if (!collapse_key_stringpool_ ||
- whitelisted_resources_.find(entry->name) != whitelisted_resources_.end()) {
+ name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
} else {
- // resource isn't whitelisted, add it as obfuscated value
+ // resource isn't exempt from collapse, add it as obfuscated value
local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
}
// Group values by configuration.
@@ -712,7 +714,7 @@ class PackageFlattener {
StringPool type_pool_;
StringPool key_pool_;
bool collapse_key_stringpool_;
- const std::set<std::string>& whitelisted_resources_;
+ const std::set<ResourceName>& name_collapse_exemptions_;
};
} // namespace
@@ -760,7 +762,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
PackageFlattener flattener(context, package.get(), &table->included_packages_,
options_.use_sparse_entries, options_.collapse_key_stringpool,
- options_.whitelisted_resources);
+ options_.name_collapse_exemptions);
if (!flattener.FlattenPackage(&package_buffer)) {
return false;
}
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 73c17295556b..4360db190146 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -19,6 +19,7 @@
#include "android-base/macros.h"
+#include "Resource.h"
#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
@@ -41,8 +42,8 @@ struct TableFlattenerOptions {
// have name indices that point to this single value
bool collapse_key_stringpool = false;
- // Set of whitelisted resource names to avoid altering in key stringpool
- std::set<std::string> whitelisted_resources;
+ // Set of resources to avoid collapsing to a single entry in key stringpool.
+ std::set<ResourceName> name_collapse_exemptions;
// Map from original resource paths to shortened resource paths.
std::map<std::string, std::string> shortened_path_map;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index a9409235e07a..8fbdd7f27041 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -518,7 +518,7 @@ TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) {
ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result));
}
-TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
@@ -572,7 +572,7 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}
-TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
@@ -591,21 +591,22 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
TableFlattenerOptions options;
options.collapse_key_stringpool = true;
- options.whitelisted_resources.insert("test");
- options.whitelisted_resources.insert("three");
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one"));
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test"));
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one",
ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
- Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+ ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ // Note that this resource is also named "one", but it's a different type, so gets obfuscated.
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
ResTable_config::CONFIG_VERSION));