summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk3
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt33
-rw-r--r--api/test-current.txt2
-rw-r--r--core/java/android/app/ActivityThread.java23
-rw-r--r--core/java/android/app/IActivityManager.aidl5
-rw-r--r--core/java/android/app/IApplicationThread.aidl4
-rw-r--r--core/java/android/app/ResourcesManager.java2
-rw-r--r--core/java/android/app/ServiceStartArgs.aidl (renamed from core/java/android/service/resolver/ResolverTarget.aidl)8
-rw-r--r--core/java/android/app/ServiceStartArgs.java82
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java8
-rw-r--r--core/java/android/app/assist/AssistStructure.java41
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java41
-rw-r--r--core/java/android/content/pm/BaseParceledListSlice.java12
-rw-r--r--core/java/android/service/autofill/AutoFillService.java24
-rw-r--r--core/java/android/service/autofill/AutofillService.java2
-rw-r--r--core/java/android/service/autofill/AutofillServiceInfo.java22
-rw-r--r--core/java/android/service/autofill/Dataset.java11
-rw-r--r--core/java/android/service/autofill/SaveInfo.java21
-rw-r--r--core/java/android/service/resolver/IResolverRankerResult.aidl27
-rw-r--r--core/java/android/service/resolver/IResolverRankerService.aidl29
-rw-r--r--core/java/android/service/resolver/ResolverRankerService.java187
-rw-r--r--core/java/android/service/resolver/ResolverTarget.java216
-rw-r--r--core/java/android/view/autofill/AutoFillId.aidl20
-rw-r--r--core/java/android/view/autofill/AutoFillId.java85
-rw-r--r--core/java/android/view/autofill/AutoFillType.java188
-rw-r--r--core/java/android/view/autofill/AutoFillValue.java181
-rw-r--r--core/java/com/android/internal/app/LRResolverRankerService.java199
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java3
-rw-r--r--core/java/com/android/internal/app/ResolverComparator.java516
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java44
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl4
-rw-r--r--core/res/AndroidManifest.xml17
-rw-r--r--core/res/res/layout/notification_template_header.xml19
-rw-r--r--core/res/res/layout/notification_template_material_ambient.xml3
-rw-r--r--core/res/res/values/attrs.xml14
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/styles_material.xml12
-rw-r--r--core/res/res/values/themes_material.xml13
-rw-r--r--packages/CarrierDefaultApp/res/values/strings.xml1
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java51
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java9
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java87
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java145
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java20
-rw-r--r--telecomm/java/android/telecom/Call.java62
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java17
-rw-r--r--tools/aapt2/ResourceParser.cpp12
-rw-r--r--tools/aapt2/ResourceParser_test.cpp12
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator.cpp293
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator_test.cpp146
73 files changed, 1503 insertions, 2214 deletions
diff --git a/Android.mk b/Android.mk
index e27aa307e1e6..ee7d47103f6a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -320,8 +320,6 @@ LOCAL_SRC_FILES += \
core/java/android/service/wallpaper/IWallpaperService.aidl \
core/java/android/service/chooser/IChooserTargetService.aidl \
core/java/android/service/chooser/IChooserTargetResult.aidl \
- core/java/android/service/resolver/IResolverRankerService.aidl \
- core/java/android/service/resolver/IResolverRankerResult.aidl \
core/java/android/text/ITextClassificationService.aidl \
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
@@ -730,7 +728,6 @@ aidl_files := \
frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \
frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
- frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \
frameworks/base/core/java/android/speech/tts/Voice.aidl \
frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \
frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \
diff --git a/api/current.txt b/api/current.txt
index 580cb247f760..c5f4816474dc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7044,6 +7044,7 @@ package android.appwidget {
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
method public android.os.Bundle getAppWidgetOptions(int);
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+ method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle);
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
method public boolean isRequestPinAppWidgetSupported();
@@ -38750,6 +38751,7 @@ package android.telecom {
method public java.lang.String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
method public final long getConnectTimeMillis();
+ method public long getCreationTimeMillis();
method public android.telecom.DisconnectCause getDisconnectCause();
method public android.os.Bundle getExtras();
method public android.telecom.GatewayInfo getGatewayInfo();
diff --git a/api/system-current.txt b/api/system-current.txt
index ed87cb339042..9b295bfe95d6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -53,7 +53,6 @@ package android {
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
- field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
@@ -7504,6 +7503,7 @@ package android.appwidget {
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
method public android.os.Bundle getAppWidgetOptions(int);
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+ method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle);
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
method public boolean isRequestPinAppWidgetSupported();
@@ -40620,36 +40620,6 @@ package android.service.quicksettings {
}
-package android.service.resolver {
-
- public abstract class ResolverRankerService extends android.app.Service {
- ctor public ResolverRankerService();
- method public android.os.IBinder onBind(android.content.Intent);
- method public void onPredictSharingProbabilities(java.util.List<android.service.resolver.ResolverTarget>);
- method public void onTrainRankingModel(java.util.List<android.service.resolver.ResolverTarget>, int);
- field public static final java.lang.String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService";
- }
-
- public final class ResolverTarget implements android.os.Parcelable {
- ctor public ResolverTarget();
- method public int describeContents();
- method public float getChooserScore();
- method public float getLaunchScore();
- method public float getRecencyScore();
- method public float getSelectProbability();
- method public float getTimeSpentScore();
- method public void setChooserScore(float);
- method public void setLaunchScore(float);
- method public void setRecencyScore(float);
- method public void setSelectProbability(float);
- method public void setTimeSpentScore(float);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.resolver.ResolverTarget> CREATOR;
- }
-
-}
-
package android.service.restrictions {
public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
@@ -41973,6 +41943,7 @@ package android.telecom {
method public java.lang.String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
method public final long getConnectTimeMillis();
+ method public long getCreationTimeMillis();
method public android.telecom.DisconnectCause getDisconnectCause();
method public android.os.Bundle getExtras();
method public android.telecom.GatewayInfo getGatewayInfo();
diff --git a/api/test-current.txt b/api/test-current.txt
index c7f2a9f8df41..6db91be96ca6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7074,6 +7074,7 @@ package android.appwidget {
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
method public android.os.Bundle getAppWidgetOptions(int);
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+ method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle);
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
method public boolean isRequestPinAppWidgetSupported();
@@ -38954,6 +38955,7 @@ package android.telecom {
method public java.lang.String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
method public final long getConnectTimeMillis();
+ method public long getCreationTimeMillis();
method public android.telecom.DisconnectCause getDisconnectCause();
method public android.os.Bundle getExtras();
method public android.telecom.GatewayInfo getGatewayInfo();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d1d462c2fbe8..7299d6bd0ed7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,6 +38,7 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.AssetManager;
@@ -869,16 +870,20 @@ public final class ActivityThread {
sendMessage(H.UNBIND_SERVICE, s);
}
- public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
- int flags ,Intent args) {
- ServiceArgsData s = new ServiceArgsData();
- s.token = token;
- s.taskRemoved = taskRemoved;
- s.startId = startId;
- s.flags = flags;
- s.args = args;
+ public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
+ List<ServiceStartArgs> list = args.getList();
+
+ for (int i = 0; i < list.size(); i++) {
+ ServiceStartArgs ssa = list.get(i);
+ ServiceArgsData s = new ServiceArgsData();
+ s.token = token;
+ s.taskRemoved = ssa.taskRemoved;
+ s.startId = ssa.startId;
+ s.flags = ssa.flags;
+ s.args = ssa.args;
- sendMessage(H.SERVICE_ARGS, s);
+ sendMessage(H.SERVICE_ARGS, s);
+ }
}
public final void scheduleStopService(IBinder token) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f4d26fd7d9d8..079bbcdd4951 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -635,6 +635,11 @@ interface IActivityManager {
*/
int getLastResumedActivityUserId();
+ /**
+ * Add a bare uid to the background restrictions whitelist. Only the system uid may call this.
+ */
+ void backgroundWhitelistUid(int uid);
+
// WARNING: when these transactions are updated, check if they are any callers on the native
// side. If so, make sure they are using the correct transaction ids and arguments.
// If a transaction which will also be used on the native side is being inserted, add it
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 6c43fe3beca1..1b3c00b61539 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -25,6 +25,7 @@ import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
@@ -86,8 +87,7 @@ oneway interface IApplicationThread {
in Bundle coreSettings, in String buildSerial);
void scheduleExit();
void scheduleConfigurationChanged(in Configuration config);
- void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
- int flags, in Intent args);
+ void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
void updateTimeZone();
void processInBackground();
void scheduleBindService(IBinder token,
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 489a0f0975ae..d620a81e985b 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -945,7 +945,7 @@ public class ResourcesManager {
final ResourcesKey key = mResourceImpls.keyAt(i);
final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl != null && key.mResDir.equals(assetPath)) {
+ if (impl != null && Objects.equals(key.mResDir, assetPath)) {
if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
final int newLibAssetCount = 1 +
(key.mLibDirs != null ? key.mLibDirs.length : 0);
diff --git a/core/java/android/service/resolver/ResolverTarget.aidl b/core/java/android/app/ServiceStartArgs.aidl
index 6cab2d4df908..fd2cf0f93dc6 100644
--- a/core/java/android/service/resolver/ResolverTarget.aidl
+++ b/core/java/android/app/ServiceStartArgs.aidl
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package android.service.resolver;
+package android.app;
-/**
- * @hide
- */
-parcelable ResolverTarget;
+/** @hide */
+parcelable ServiceStartArgs;
diff --git a/core/java/android/app/ServiceStartArgs.java b/core/java/android/app/ServiceStartArgs.java
new file mode 100644
index 000000000000..f030cbaaecaf
--- /dev/null
+++ b/core/java/android/app/ServiceStartArgs.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes a Service.onStartCommand() request from the system.
+ * @hide
+ */
+public class ServiceStartArgs implements Parcelable {
+ final public boolean taskRemoved;
+ final public int startId;
+ final public int flags;
+ final public Intent args;
+
+ public ServiceStartArgs(boolean _taskRemoved, int _startId, int _flags, Intent _args) {
+ taskRemoved = _taskRemoved;
+ startId = _startId;
+ flags = _flags;
+ args = _args;
+ }
+
+ public String toString() {
+ return "ServiceStartArgs{taskRemoved=" + taskRemoved + ", startId=" + startId
+ + ", flags=0x" + Integer.toHexString(flags) + ", args=" + args + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(taskRemoved ? 1 : 0);
+ out.writeInt(startId);
+ out.writeInt(flags);
+ if (args != null) {
+ out.writeInt(1);
+ args.writeToParcel(out, 0);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<ServiceStartArgs> CREATOR
+ = new Parcelable.Creator<ServiceStartArgs>() {
+ public ServiceStartArgs createFromParcel(Parcel in) {
+ return new ServiceStartArgs(in);
+ }
+
+ public ServiceStartArgs[] newArray(int size) {
+ return new ServiceStartArgs[size];
+ }
+ };
+
+ public ServiceStartArgs(Parcel in) {
+ taskRemoved = in.readInt() != 0;
+ startId = in.readInt();
+ flags = in.readInt();
+ if (in.readInt() != 0) {
+ args = Intent.CREATOR.createFromParcel(in);
+ } else {
+ args = null;
+ }
+ }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bb5d830820aa..82ad8252c5a9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2768,9 +2768,11 @@ public class DevicePolicyManager {
* or clears the lockscreen password.
* <p>
* <em>This token is highly sensitive and should be treated at the same level as user
- * credentials. In particular, NEVER store this token on device in plaintext, especially in
- * Device-Encrypted storage if the token will be used to reset password on FBE devices before
- * user unlocks.
+ * credentials. In particular, NEVER store this token on device in plaintext. Do not store
+ * the plaintext token in device-encrypted storage if it will be needed to reset password on
+ * file-based encryption devices before user unlocks. Consider carefully how any password token
+ * will be stored on your server and who will need access to them. Tokens may be the subject of
+ * legal access requests.
* </em>
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7d2db5c9f384..fe51633b50eb 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -27,9 +27,6 @@ import android.view.ViewStructure.HtmlInfo;
import android.view.ViewStructure.HtmlInfo.Builder;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillType;
-import android.view.autofill.AutoFillValue;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
@@ -920,15 +917,6 @@ public class AssistStructure implements Parcelable {
}
/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
- */
- @Deprecated
- public AutoFillId getAutoFillId() {
- return AutoFillId.forDaRealId(mAutofillId);
- }
-
- /**
* Gets the id that can be used to autofill the view contents.
*
* <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not
@@ -939,26 +927,6 @@ public class AssistStructure implements Parcelable {
}
/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype()
- */
- @Deprecated
- public AutoFillType getAutoFillType() {
- switch (getAutofillType()) {
- case View.AUTOFILL_TYPE_TEXT:
- return AutoFillType.forText();
- case View.AUTOFILL_TYPE_TOGGLE:
- return AutoFillType.forToggle();
- case View.AUTOFILL_TYPE_LIST:
- return AutoFillType.forList();
- case View.AUTOFILL_TYPE_DATE:
- return AutoFillType.forDate();
- default:
- return null;
- }
- }
-
- /**
* Gets the the type of value that can be used to autofill the view contents.
*
* <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not
@@ -982,15 +950,6 @@ public class AssistStructure implements Parcelable {
}
/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
- */
- @Deprecated
- public AutoFillValue getAutoFillValue() {
- return AutoFillValue.forDaRealValue(mAutofillValue);
- }
-
- /**
* Gets the the value of this view.
*
* <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 74a39e85e029..9f35e85b0665 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -710,9 +710,9 @@ public class AppWidgetManager {
* user may have a corporate profile. In this case the parent user profile has a
* child profile, the corporate one.
*
- * @param profile The profile for which to get providers. Passing null is equivaled
- * to passing only the current user handle.
- * @return The intalled providers.
+ * @param profile The profile for which to get providers. Passing null is equivalent
+ * to querying for only the calling user.
+ * @return The installed providers.
*
* @see android.os.Process#myUserHandle()
* @see android.os.UserManager#getUserProfiles()
@@ -722,7 +722,31 @@ public class AppWidgetManager {
return Collections.emptyList();
}
return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
- profile);
+ profile, null);
+ }
+
+ /**
+ * Gets the AppWidget providers for the given package and user profile. User
+ * profile can only be the current user or a profile of the current user. For
+ * example, the current user may have a corporate profile. In this case the
+ * parent user profile has a child profile, the corporate one.
+ *
+ * @param packageName The package for which to get providers. If null, this method is
+ * equivalent to {@link #getInstalledProvidersForProfile(UserHandle)}.
+ * @param profile The profile for which to get providers. Passing null is equivalent
+ * to querying for only the calling user.
+ * @return The installed providers.
+ *
+ * @see android.os.Process#myUserHandle()
+ * @see android.os.UserManager#getUserProfiles()
+ */
+ public List<AppWidgetProviderInfo> getInstalledProvidersForPackage(@Nullable String packageName,
+ @Nullable UserHandle profile) {
+ if (mService == null) {
+ return Collections.emptyList();
+ }
+ return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ profile, packageName);
}
/**
@@ -733,7 +757,7 @@ public class AppWidgetManager {
return Collections.emptyList();
}
return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
- null);
+ null, null);
}
/**
@@ -752,7 +776,7 @@ public class AppWidgetManager {
if (mService == null) {
return Collections.emptyList();
}
- return getInstalledProvidersForProfile(categoryFilter, null);
+ return getInstalledProvidersForProfile(categoryFilter, null, null);
}
/**
@@ -766,6 +790,7 @@ public class AppWidgetManager {
* @param profile A profile of the current user which to be queried. The user
* is itself also a profile. If null, the providers only for the current user
* are returned.
+ * @param packageName If specified, will only return providers from the given package.
* @return The intalled providers.
*
* @see android.os.Process#myUserHandle()
@@ -774,7 +799,7 @@ public class AppWidgetManager {
* @hide
*/
public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
- UserHandle profile) {
+ @Nullable UserHandle profile, @Nullable String packageName) {
if (mService == null) {
return Collections.emptyList();
}
@@ -785,7 +810,7 @@ public class AppWidgetManager {
try {
ParceledListSlice<AppWidgetProviderInfo> providers = mService.getInstalledProvidersForProfile(
- categoryFilter, profile.getIdentifier());
+ categoryFilter, profile.getIdentifier(), packageName);
if (providers == null) {
return Collections.emptyList();
}
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index c4e4e06be749..aaa5f19c3fca 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -50,6 +50,8 @@ abstract class BaseParceledListSlice<T> implements Parcelable {
private final List<T> mList;
+ private int mInlineCountLimit = Integer.MAX_VALUE;
+
public BaseParceledListSlice(List<T> list) {
mList = list;
}
@@ -135,6 +137,14 @@ abstract class BaseParceledListSlice<T> implements Parcelable {
}
/**
+ * Set a limit on the maximum number of entries in the array that will be included
+ * inline in the initial parcelling of this object.
+ */
+ public void setInlineCountLimit(int maxCount) {
+ mInlineCountLimit = maxCount;
+ }
+
+ /**
* Write this to another Parcel. Note that this discards the internal Parcel
* and should not be used anymore. This is so we can pass this to a Binder
* where we won't have a chance to call recycle on this.
@@ -149,7 +159,7 @@ abstract class BaseParceledListSlice<T> implements Parcelable {
final Class<?> listElementClass = mList.get(0).getClass();
writeParcelableCreator(mList.get(0), dest);
int i = 0;
- while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
+ while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
dest.writeInt(1);
final T parcelable = mList.get(i);
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
deleted file mode 100644
index c26f679cbda8..000000000000
--- a/core/java/android/service/autofill/AutoFillService.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.service.autofill;
-
-/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use AutofillService
- */
-@Deprecated
-public abstract class AutoFillService extends AutofillService {
-}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 6f17d0e5ec77..9f8a6217c5f4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -71,7 +71,7 @@ public abstract class AutofillService extends Service {
* Name under which a AutoFillService component publishes information about itself.
* This meta-data should reference an XML resource containing a
* <code>&lt;{@link
- * android.R.styleable#AutoFillService autofill-service}&gt;</code> tag.
+ * android.R.styleable#AutofillService autofill-service}&gt;</code> tag.
* This is a a sample XML file configuring an AutoFillService:
* <pre> &lt;autofill-service
* android:settingsActivity="foo.bar.SettingsActivity"
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index f6d40dbf3414..0f4824e47fa9 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -36,7 +36,6 @@ import com.android.internal.R;
import java.io.IOException;
-// TODO(b/33197203 , b/33802548): add CTS tests
/**
* {@link ServiceInfo} and meta-data about an {@link AutofillService}.
*
@@ -75,15 +74,8 @@ public final class AutofillServiceInfo {
mServiceInfo = si;
final TypedArray metaDataArray = getMetaDataArray(pm, si);
if (metaDataArray != null) {
- // TODO(b/35956626): inline newSettingsActivity once clients migrate
- final String newSettingsActivity =
- metaDataArray.getString(R.styleable.AutofillService_settingsActivity);
- if (newSettingsActivity != null) {
- mSettingsActivity = newSettingsActivity;
- } else {
- mSettingsActivity =
- metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
- }
+ mSettingsActivity = metaDataArray
+ .getString(R.styleable.AutofillService_settingsActivity);
metaDataArray.recycle();
} else {
mSettingsActivity = null;
@@ -96,16 +88,11 @@ public final class AutofillServiceInfo {
@Nullable
private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
// Check for permissions.
- // TODO(b/35956626): remove check for BIND_AUTO_FILL once clients migrate
- if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission)
- && !Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
+ if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission)) {
Log.e(TAG, "Service does not require permission " + Manifest.permission.BIND_AUTOFILL);
return null;
}
- // TODO(b/35956626): remove once clients migrate
- final boolean oldStyle = !Manifest.permission.BIND_AUTOFILL.equals(si.permission);
-
// Get the AutoFill metadata, if declared.
XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA);
if (parser == null) {
@@ -141,8 +128,7 @@ public final class AutofillServiceInfo {
return null;
}
- return oldStyle ? res.obtainAttributes(attrs, R.styleable.AutoFillService)
- : res.obtainAttributes(attrs, R.styleable.AutofillService);
+ return res.obtainAttributes(attrs, R.styleable.AutofillService);
} finally {
parser.close();
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index b7a04206a4c6..e77bd0d753ac 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -23,8 +23,6 @@ import android.annotation.Nullable;
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillValue;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
@@ -175,15 +173,6 @@ public final class Dataset implements Parcelable {
}
/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use other setValue()
- */
- @Deprecated
- public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
- return setValue(id.getDaRealId(), value.getDaRealValue());
- }
-
- /**
* Sets the value of a field.
*
* @param id id returned by {@link
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 6213d27bfc70..95608a54a72c 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,7 +25,6 @@ import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.autofill.AutoFillId;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -269,26 +268,6 @@ public final class SaveInfo implements Parcelable {
return this;
}
-
- /**
- * @hide
- */
- // TODO(b/33197203): temporary fix to runtime crash
- public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) {
- throwIfDestroyed();
-
- if (ids == null || ids.length == 0) {
- return this;
- }
- if (mRequiredIds == null) {
- mRequiredIds = new AutofillId[ids.length];
- }
- for (int i = 0; i < ids.length; i++) {
- mRequiredIds[i] = ids[i].getDaRealId();
- }
- return this;
- }
-
/**
* Sets an optional description to be shown in the UI when the user is asked to save.
*
diff --git a/core/java/android/service/resolver/IResolverRankerResult.aidl b/core/java/android/service/resolver/IResolverRankerResult.aidl
deleted file mode 100644
index bda315420b7b..000000000000
--- a/core/java/android/service/resolver/IResolverRankerResult.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.resolver;
-
-import android.service.resolver.ResolverTarget;
-
-/**
- * @hide
- */
-oneway interface IResolverRankerResult
-{
- void sendResult(in List<ResolverTarget> results);
-} \ No newline at end of file
diff --git a/core/java/android/service/resolver/IResolverRankerService.aidl b/core/java/android/service/resolver/IResolverRankerService.aidl
deleted file mode 100644
index f0d747d974a7..000000000000
--- a/core/java/android/service/resolver/IResolverRankerService.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.resolver;
-
-import android.service.resolver.IResolverRankerResult;
-import android.service.resolver.ResolverTarget;
-
-/**
- * @hide
- */
-oneway interface IResolverRankerService
-{
- void predict(in List<ResolverTarget> targets, IResolverRankerResult result);
- void train(in List<ResolverTarget> targets, int selectedPosition);
-} \ No newline at end of file
diff --git a/core/java/android/service/resolver/ResolverRankerService.java b/core/java/android/service/resolver/ResolverRankerService.java
deleted file mode 100644
index 05067479bf45..000000000000
--- a/core/java/android/service/resolver/ResolverRankerService.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.resolver;
-
-import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.RemoteException;
-import android.service.resolver.ResolverTarget;
-import android.util.Log;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * A service to rank apps according to usage stats of apps, when the system is resolving targets for
- * an Intent.
- *
- * <p>To extend this class, you must declare the service in your manifest file with the
- * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an
- * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
- * <pre>
- * &lt;service android:name=".MyResolverRankerService"
- * android:exported="true"
- * android:priority="100"
- * android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"&gt;
- * &lt;intent-filter&gt;
- * &lt;action android:name="android.service.resolver.ResolverRankerService" /&gt;
- * &lt;/intent-filter&gt;
- * &lt;/service&gt;
- * </pre>
- * @hide
- */
-@SystemApi
-public abstract class ResolverRankerService extends Service {
-
- private static final String TAG = "ResolverRankerService";
-
- private static final boolean DEBUG = false;
-
- /**
- * The Intent action that a service must respond to. Add it to the intent filter of the service
- * in its manifest.
- */
- @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService";
-
- /**
- * The permission that a service must require to ensure that only Android system can bind to it.
- * If this permission is not enforced in the AndroidManifest of the service, the system will
- * skip that service.
- */
- public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
-
- private ResolverRankerServiceWrapper mWrapper = null;
-
- /**
- * Called by the system to retrieve a list of probabilities to rank apps/options. To implement
- * it, set selectProbability of each input {@link ResolverTarget}. The higher the
- * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the
- * user. Override this function to provide prediction results.
- *
- * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked.
- *
- * @throws Exception when the prediction task fails.
- */
- public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {}
-
- /**
- * Called by the system to train/update a ranking service, after the user makes a selection from
- * the ranked list of apps. Override this function to enable model updates.
- *
- * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked.
- * @param selectedPosition the position of the selected app in the list.
- *
- * @throws Exception when the training task fails.
- */
- public void onTrainRankingModel(
- final List<ResolverTarget> targets, final int selectedPosition) {}
-
- private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE";
- private volatile Handler mHandler;
- private HandlerThread mHandlerThread;
-
- @Override
- public IBinder onBind(Intent intent) {
- if (DEBUG) Log.d(TAG, "onBind " + intent);
- if (!SERVICE_INTERFACE.equals(intent.getAction())) {
- if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null");
- return null;
- }
- if (mHandlerThread == null) {
- mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
- }
- if (mWrapper == null) {
- mWrapper = new ResolverRankerServiceWrapper();
- }
- return mWrapper;
- }
-
- @Override
- public void onDestroy() {
- mHandler = null;
- if (mHandlerThread != null) {
- mHandlerThread.quitSafely();
- }
- super.onDestroy();
- }
-
- private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) {
- try {
- result.sendResult(targets);
- } catch (Exception e) {
- Log.e(TAG, "failed to send results: " + e);
- }
- }
-
- private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub {
-
- @Override
- public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result)
- throws RemoteException {
- Runnable predictRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- if (DEBUG) {
- Log.d(TAG, "predict calls onPredictSharingProbabilities.");
- }
- onPredictSharingProbabilities(targets);
- sendResult(targets, result);
- } catch (Exception e) {
- Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e);
- sendResult(null, result);
- }
- }
- };
- final Handler h = mHandler;
- if (h != null) {
- h.post(predictRunnable);
- }
- }
-
- @Override
- public void train(final List<ResolverTarget> targets, final int selectedPosition)
- throws RemoteException {
- Runnable trainRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- if (DEBUG) {
- Log.d(TAG, "train calls onTranRankingModel");
- }
- onTrainRankingModel(targets, selectedPosition);
- } catch (Exception e) {
- Log.e(TAG, "onTrainRankingModel failed; skip train: " + e);
- }
- }
- };
- final Handler h = mHandler;
- if (h != null) {
- h.post(trainRunnable);
- }
- }
- }
-}
diff --git a/core/java/android/service/resolver/ResolverTarget.java b/core/java/android/service/resolver/ResolverTarget.java
deleted file mode 100644
index fb3e2d738469..000000000000
--- a/core/java/android/service/resolver/ResolverTarget.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.resolver;
-
-import android.annotation.SystemApi;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-
-import java.util.Map;
-
-/**
- * A ResolverTarget contains features by which an app or option will be ranked, in
- * {@link ResolverRankerService}.
- * @hide
- */
-@SystemApi
-public final class ResolverTarget implements Parcelable {
- private static final String TAG = "ResolverTarget";
-
- /**
- * a float score for recency of last use.
- */
- private float mRecencyScore;
-
- /**
- * a float score for total time spent.
- */
- private float mTimeSpentScore;
-
- /**
- * a float score for number of launches.
- */
- private float mLaunchScore;
-
- /**
- * a float score for number of selected.
- */
- private float mChooserScore;
-
- /**
- * a float score for the probability to be selected.
- */
- private float mSelectProbability;
-
- // constructor for the class.
- public ResolverTarget() {}
-
- ResolverTarget(Parcel in) {
- mRecencyScore = in.readFloat();
- mTimeSpentScore = in.readFloat();
- mLaunchScore = in.readFloat();
- mChooserScore = in.readFloat();
- mSelectProbability = in.readFloat();
- }
-
- /**
- * Gets the score for how recently the target was used in the foreground.
- *
- * @return a float score whose range is [0, 1]. The higher the score is, the more recently the
- * target was used.
- */
- public float getRecencyScore() {
- return mRecencyScore;
- }
-
- /**
- * Sets the score for how recently the target was used in the foreground.
- *
- * @param recencyScore a float score whose range is [0, 1]. The higher the score is, the more
- * recently the target was used.
- */
- public void setRecencyScore(float recencyScore) {
- this.mRecencyScore = recencyScore;
- }
-
- /**
- * Gets the score for how long the target has been used in the foreground.
- *
- * @return a float score whose range is [0, 1]. The higher the score is, the longer the target
- * has been used for.
- */
- public float getTimeSpentScore() {
- return mTimeSpentScore;
- }
-
- /**
- * Sets the score for how long the target has been used in the foreground.
- *
- * @param timeSpentScore a float score whose range is [0, 1]. The higher the score is, the
- * longer the target has been used for.
- */
- public void setTimeSpentScore(float timeSpentScore) {
- this.mTimeSpentScore = timeSpentScore;
- }
-
- /**
- * Gets the score for how many times the target has been launched to the foreground.
- *
- * @return a float score whose range is [0, 1]. The higher the score is, the more times the
- * target has been launched.
- */
- public float getLaunchScore() {
- return mLaunchScore;
- }
-
- /**
- * Sets the score for how many times the target has been launched to the foreground.
- *
- * @param launchScore a float score whose range is [0, 1]. The higher the score is, the more
- * times the target has been launched.
- */
- public void setLaunchScore(float launchScore) {
- this.mLaunchScore = launchScore;
- }
-
- /**
- * Gets the score for how many times the target has been selected by the user to share the same
- * types of content.
- *
- * @return a float score whose range is [0, 1]. The higher the score is, the
- * more times the target has been selected by the user to share the same types of content for.
- */
- public float getChooserScore() {
- return mChooserScore;
- }
-
- /**
- * Sets the score for how many times the target has been selected by the user to share the same
- * types of content.
- *
- * @param chooserScore a float score whose range is [0, 1]. The higher the score is, the more
- * times the target has been selected by the user to share the same types
- * of content for.
- */
- public void setChooserScore(float chooserScore) {
- this.mChooserScore = chooserScore;
- }
-
- /**
- * Gets the probability of how likely this target will be selected by the user.
- *
- * @return a float score whose range is [0, 1]. The higher the score is, the more likely the
- * user is going to select this target.
- */
- public float getSelectProbability() {
- return mSelectProbability;
- }
-
- /**
- * Sets the probability for how like this target will be selected by the user.
- *
- * @param selectProbability a float score whose range is [0, 1]. The higher the score is, the
- * more likely tht user is going to select this target.
- */
- public void setSelectProbability(float selectProbability) {
- this.mSelectProbability = selectProbability;
- }
-
- // serialize the class to a string.
- @Override
- public String toString() {
- return "ResolverTarget{"
- + mRecencyScore + ", "
- + mTimeSpentScore + ", "
- + mLaunchScore + ", "
- + mChooserScore + ", "
- + mSelectProbability + "}";
- }
-
- // describes the kinds of special objects contained in this Parcelable instance's marshaled
- // representation.
- @Override
- public int describeContents() {
- return 0;
- }
-
- // flattens this object in to a Parcel.
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeFloat(mRecencyScore);
- dest.writeFloat(mTimeSpentScore);
- dest.writeFloat(mLaunchScore);
- dest.writeFloat(mChooserScore);
- dest.writeFloat(mSelectProbability);
- }
-
- // creator definition for the class.
- public static final Creator<ResolverTarget> CREATOR
- = new Creator<ResolverTarget>() {
- @Override
- public ResolverTarget createFromParcel(Parcel source) {
- return new ResolverTarget(source);
- }
-
- @Override
- public ResolverTarget[] newArray(int size) {
- return new ResolverTarget[size];
- }
- };
-}
diff --git a/core/java/android/view/autofill/AutoFillId.aidl b/core/java/android/view/autofill/AutoFillId.aidl
deleted file mode 100644
index fc57ce7acfc1..000000000000
--- a/core/java/android/view/autofill/AutoFillId.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-// @deprecated TODO(b/35956626): remove once clients use AutofillId
-parcelable AutoFillId; \ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java
deleted file mode 100644
index 081fb0289d76..000000000000
--- a/core/java/android/view/autofill/AutoFillId.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.autofill;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
- */
-@Deprecated
-public final class AutoFillId implements Parcelable {
-
- private final AutofillId mRealId;
-
- /** @hide */
- public AutoFillId(AutofillId daRealId) {
- this.mRealId = daRealId;
- }
-
- @Override
- public int hashCode() {
- return mRealId.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- final AutoFillId other = (AutoFillId) obj;
- return mRealId.equals(other.mRealId);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mRealId, 0);
- }
-
- private AutoFillId(Parcel parcel) {
- mRealId = parcel.readParcelable(null);
- }
-
- /** @hide */
- public AutofillId getDaRealId() {
- return mRealId;
- }
-
- /** @hide */
- public static AutoFillId forDaRealId(AutofillId id) {
- return id == null ? null : new AutoFillId(id);
- }
-
- public static final Parcelable.Creator<AutoFillId> CREATOR =
- new Parcelable.Creator<AutoFillId>() {
- @Override
- public AutoFillId createFromParcel(Parcel source) {
- return new AutoFillId(source);
- }
-
- @Override
- public AutoFillId[] newArray(int size) {
- return new AutoFillId[size];
- }
- };
-}
diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java
deleted file mode 100644
index c508ba4eb892..000000000000
--- a/core/java/android/view/autofill/AutoFillType.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-import static android.view.autofill.Helper.DEBUG;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-/**
- * Defines the type of a object that can be used to autofill a {@link View} so the
- * {@link android.service.autofill.AutofillService} can use the proper {@link AutofillValue} to
- * fill it.
- *
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
- */
-@Deprecated
-public final class AutoFillType implements Parcelable {
-
- // Cached instance for types that don't have subtype; it uses the "lazy initialization holder
- // class idiom" (Effective Java, Item 71) to avoid memory utilization when autofill is not
- // enabled.
- private static class DefaultTypesHolder {
- static final AutoFillType TEXT = new AutoFillType(TYPE_TEXT);
- static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE);
- static final AutoFillType LIST = new AutoFillType(TYPE_LIST);
- static final AutoFillType DATE = new AutoFillType(TYPE_DATE);
- }
-
- private static final int TYPE_TEXT = 1;
- private static final int TYPE_TOGGLE = 2;
- private static final int TYPE_LIST = 3;
- private static final int TYPE_DATE = 4;
-
- private final int mType;
-
- private AutoFillType(int type) {
- mType = type;
- }
-
- /**
- * Checks if this is a type for a text field, which is filled by a {@link CharSequence}.
- */
- public boolean isText() {
- return mType == TYPE_TEXT;
- }
-
- /**
- * Checks if this is a a type for a togglable field, which is filled by a {@code boolean}.
- */
- public boolean isToggle() {
- return mType == TYPE_TOGGLE;
- }
-
- /**
- * Checks if this is a type for a selection list field, which is filled by a {@code integer}
- * representing the element index inside the list (starting at {@code 0}.
- */
- public boolean isList() {
- return mType == TYPE_LIST;
- }
-
- /**
- * Checks if this is a type for a date and time, which is represented by a long representing
- * the number of milliseconds since the standard base time known as "the epoch", namely
- * January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}.
- */
- public boolean isDate() {
- return mType == TYPE_DATE;
- }
-
- /////////////////////////////////////
- // Object "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public String toString() {
- if (!DEBUG) return super.toString();
-
- return "AutoFillType [type=" + mType + "]";
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + mType;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- final AutoFillType other = (AutoFillType) obj;
- if (mType != other.mType) return false;
- return true;
- }
-
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mType);
- }
-
- private AutoFillType(Parcel parcel) {
- mType = parcel.readInt();
- }
-
- public static final Parcelable.Creator<AutoFillType> CREATOR =
- new Parcelable.Creator<AutoFillType>() {
- @Override
- public AutoFillType createFromParcel(Parcel source) {
- return new AutoFillType(source);
- }
-
- @Override
- public AutoFillType[] newArray(int size) {
- return new AutoFillType[size];
- }
- };
-
- ////////////////////
- // Factory methods //
- ////////////////////
-
- /**
- * Creates a text field type, which is filled by a {@link CharSequence}.
- *
- * <p>See {@link #isText()} for more info.
- */
- public static AutoFillType forText() {
- return DefaultTypesHolder.TEXT;
- }
-
- /**
- * Creates a type that can be toggled which is filled by a {@code boolean}.
- *
- * <p>See {@link #isToggle()} for more info.
- */
- public static AutoFillType forToggle() {
- return DefaultTypesHolder.TOGGLE;
- }
-
- /**
- * Creates a selection list, which is filled by a {@code integer} representing the element index
- * inside the list (starting at {@code 0}.
- *
- * <p>See {@link #isList()} for more info.
- */
- public static AutoFillType forList() {
- return DefaultTypesHolder.LIST;
- }
-
- /**
- * Creates a type that represents a date.
- *
- * <p>See {@link #isDate()} for more info.
- */
- public static AutoFillType forDate() {
- return DefaultTypesHolder.DATE;
- }
-}
diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
deleted file mode 100644
index 4774d8f0a71a..000000000000
--- a/core/java/android/view/autofill/AutoFillValue.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-import static android.view.autofill.Helper.DEBUG;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use AutofillValue
- */
-@Deprecated
-public final class AutoFillValue implements Parcelable {
- private final AutofillValue mRealValue;
-
- private AutoFillValue(AutofillValue daRealValue) {
- this.mRealValue = daRealValue;
- }
-
- /**
- * Gets the value to autofill a text field.
- *
- * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
- */
- public CharSequence getTextValue() {
- return mRealValue.getTextValue();
- }
-
- /**
- * Gets the value to autofill a toggable field.
- *
- * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
- */
- public boolean getToggleValue() {
- return mRealValue.getToggleValue();
- }
-
- /**
- * Gets the value to autofill a selection list field.
- *
- * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
- */
- public int getListValue() {
- return mRealValue.getListValue();
- }
-
- /**
- * Gets the value to autofill a date field.
- *
- * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
- */
- public long getDateValue() {
- return mRealValue.getDateValue();
- }
-
- /////////////////////////////////////
- // Object "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int hashCode() {
- return mRealValue.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- final AutoFillValue other = (AutoFillValue) obj;
- return mRealValue.equals(other.mRealValue);
- }
-
- @Override
- public String toString() {
- if (!DEBUG) return super.toString();
-
- return mRealValue.toString();
- }
-
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mRealValue, 0);
- }
-
- private AutoFillValue(Parcel parcel) {
- mRealValue = parcel.readParcelable(null);
- }
-
- public static final Parcelable.Creator<AutoFillValue> CREATOR =
- new Parcelable.Creator<AutoFillValue>() {
- @Override
- public AutoFillValue createFromParcel(Parcel source) {
- return new AutoFillValue(source);
- }
-
- @Override
- public AutoFillValue[] newArray(int size) {
- return new AutoFillValue[size];
- }
- };
-
- ////////////////////
- // Factory methods //
- ////////////////////
- /**
- * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a text field.
- *
- * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
- */
- @Nullable
- public static AutoFillValue forText(@Nullable CharSequence value) {
- return value == null ? null : new AutoFillValue(AutofillValue.forText(value));
- }
-
- /**
- * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a toggable
- * field.
- *
- * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
- */
- public static AutoFillValue forToggle(boolean value) {
- return new AutoFillValue(AutofillValue.forToggle(value));
- }
-
- /**
- * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a selection
- * list.
- *
- * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
- */
- public static AutoFillValue forList(int value) {
- return new AutoFillValue(AutofillValue.forList(value));
- }
-
- /**
- * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a date.
- *
- * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
- */
- public static AutoFillValue forDate(long date) {
- return new AutoFillValue(AutofillValue.forDate(date));
- }
-
- /** @hide */
- public static AutoFillValue forDaRealValue(AutofillValue daRealValue) {
- return new AutoFillValue(daRealValue);
- }
-
- /** @hide */
- public AutofillValue getDaRealValue() {
- return mRealValue;
- }
-}
diff --git a/core/java/com/android/internal/app/LRResolverRankerService.java b/core/java/com/android/internal/app/LRResolverRankerService.java
deleted file mode 100644
index 1cad7c770b7c..000000000000
--- a/core/java/com/android/internal/app/LRResolverRankerService.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2017 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.internal.app;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.storage.StorageManager;
-import android.service.resolver.ResolverRankerService;
-import android.service.resolver.ResolverTarget;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used
- * in {@link ResolverComparator}.
- */
-public final class LRResolverRankerService extends ResolverRankerService {
- private static final String TAG = "LRResolverRankerService";
-
- private static final boolean DEBUG = false;
-
- private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
- private static final String BIAS_PREF_KEY = "bias";
- private static final String VERSION_PREF_KEY = "version";
-
- private static final String LAUNCH_SCORE = "launch";
- private static final String TIME_SPENT_SCORE = "timeSpent";
- private static final String RECENCY_SCORE = "recency";
- private static final String CHOOSER_SCORE = "chooser";
-
- // parameters for a pre-trained model, to initialize the app ranker. When updating the
- // pre-trained model, please update these params, as well as initModel().
- private static final int CURRENT_VERSION = 1;
- private static final float LEARNING_RATE = 0.0001f;
- private static final float REGULARIZER_PARAM = 0.0001f;
-
- private SharedPreferences mParamSharedPref;
- private ArrayMap<String, Float> mFeatureWeights;
- private float mBias;
-
- @Override
- public IBinder onBind(Intent intent) {
- initModel();
- return super.onBind(intent);
- }
-
- @Override
- public void onPredictSharingProbabilities(List<ResolverTarget> targets) {
- final int size = targets.size();
- for (int i = 0; i < size; ++i) {
- ResolverTarget target = targets.get(i);
- ArrayMap<String, Float> features = getFeatures(target);
- target.setSelectProbability(predict(features));
- }
- }
-
- @Override
- public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) {
- final int size = targets.size();
- if (selectedPosition < 0 || selectedPosition >= size) {
- if (DEBUG) {
- Log.d(TAG, "Invalid Position of Selected App " + selectedPosition);
- }
- return;
- }
- final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition));
- final float positiveProbability = targets.get(selectedPosition).getSelectProbability();
- final int targetSize = targets.size();
- for (int i = 0; i < targetSize; ++i) {
- if (i == selectedPosition) {
- continue;
- }
- final ArrayMap<String, Float> negative = getFeatures(targets.get(i));
- final float negativeProbability = targets.get(i).getSelectProbability();
- if (negativeProbability > positiveProbability) {
- update(negative, negativeProbability, false);
- update(positive, positiveProbability, true);
- }
- }
- commitUpdate();
- }
-
- private void initModel() {
- mParamSharedPref = getParamSharedPref();
- mFeatureWeights = new ArrayMap<>(4);
- if (mParamSharedPref == null ||
- mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
- // Initializing the app ranker to a pre-trained model. When updating the pre-trained
- // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
- // REGULARIZER_PARAM.
- mBias = -1.6568f;
- mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
- mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
- mFeatureWeights.put(RECENCY_SCORE, 0.269f);
- mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
- } else {
- mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
- mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
- mFeatureWeights.put(
- TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
- mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
- mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
- }
- }
-
- private ArrayMap<String, Float> getFeatures(ResolverTarget target) {
- ArrayMap<String, Float> features = new ArrayMap<>(4);
- features.put(RECENCY_SCORE, target.getRecencyScore());
- features.put(TIME_SPENT_SCORE, target.getTimeSpentScore());
- features.put(LAUNCH_SCORE, target.getLaunchScore());
- features.put(CHOOSER_SCORE, target.getChooserScore());
- return features;
- }
-
- private float predict(ArrayMap<String, Float> target) {
- if (target == null) {
- return 0.0f;
- }
- final int featureSize = target.size();
- float sum = 0.0f;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- sum += weight * target.valueAt(i);
- }
- return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
- }
-
- private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
- if (target == null) {
- return;
- }
- final int featureSize = target.size();
- float error = isSelected ? 1.0f - predict : -predict;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- mBias += LEARNING_RATE * error;
- currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
- LEARNING_RATE * error * target.valueAt(i);
- mFeatureWeights.put(featureName, currentWeight);
- }
- if (DEBUG) {
- Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
- }
- }
-
- private void commitUpdate() {
- try {
- SharedPreferences.Editor editor = mParamSharedPref.edit();
- editor.putFloat(BIAS_PREF_KEY, mBias);
- final int size = mFeatureWeights.size();
- for (int i = 0; i < size; i++) {
- editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
- }
- editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
- editor.apply();
- } catch (Exception e) {
- Log.e(TAG, "Failed to commit update" + e);
- }
- }
-
- private SharedPreferences getParamSharedPref() {
- // The package info in the context isn't initialized in the way it is for normal apps,
- // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
- // build the path manually below using the same policy that appears in ContextImpl.
- if (DEBUG) {
- Log.d(TAG, "Context Package Name: " + getPackageName());
- }
- final File prefsFile = new File(new File(
- Environment.getDataUserCePackageDirectory(
- StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
- "shared_prefs"),
- PARAM_SHARED_PREF_NAME + ".xml");
- return getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
- }
-} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 622b70843cc2..3f1c9adb1b68 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -530,9 +530,6 @@ public class ResolverActivity extends Activity {
getMainThreadHandler().removeCallbacks(mPostListReadyRunnable);
mPostListReadyRunnable = null;
}
- if (mAdapter != null && mAdapter.mResolverListController != null) {
- mAdapter.mResolverListController.destroy();
- }
}
@Override
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 73b62a5fe60d..096fcb83e755 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -26,34 +26,20 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.SharedPreferences;
-import android.content.ServiceConnection;
import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
import android.os.storage.StorageManager;
import android.os.UserHandle;
-import android.service.resolver.IResolverRankerService;
-import android.service.resolver.IResolverRankerResult;
-import android.service.resolver.ResolverRankerService;
-import android.service.resolver.ResolverTarget;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import java.io.File;
-import java.lang.InterruptedException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -75,15 +61,11 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private static final float RECENCY_MULTIPLIER = 2.f;
- // message types
- private static final int RESOLVER_RANKER_SERVICE_RESULT = 0;
- private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1;
-
- // timeout for establishing connections with a ResolverRankerService.
- private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
- // timeout for establishing connections with a ResolverRankerService, collecting features and
- // predicting ranking scores.
- private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
+ // feature names used in ranking.
+ private static final String LAUNCH_SCORE = "launch";
+ private static final String TIME_SPENT_SCORE = "timeSpent";
+ private static final String RECENCY_SCORE = "recency";
+ private static final String CHOOSER_SCORE = "chooser";
private final Collator mCollator;
private final boolean mHttp;
@@ -92,74 +74,18 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private final Map<String, UsageStats> mStats;
private final long mCurrentTime;
private final long mSinceTime;
- private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>();
+ private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
private final String mReferrerPackage;
- private final Object mLock = new Object();
- private ArrayList<ResolverTarget> mTargets;
private String mContentType;
private String[] mAnnotations;
private String mAction;
- private IResolverRankerService mRanker;
- private ResolverRankerServiceConnection mConnection;
- private AfterCompute mAfterCompute;
- private Context mContext;
- private CountDownLatch mConnectSignal;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case RESOLVER_RANKER_SERVICE_RESULT:
- if (DEBUG) {
- Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT");
- }
- if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) {
- if (msg.obj != null) {
- final List<ResolverTarget> receivedTargets =
- (List<ResolverTarget>) msg.obj;
- if (receivedTargets != null && mTargets != null
- && receivedTargets.size() == mTargets.size()) {
- final int size = mTargets.size();
- for (int i = 0; i < size; ++i) {
- mTargets.get(i).setSelectProbability(
- receivedTargets.get(i).getSelectProbability());
- }
- } else {
- Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
- }
- } else {
- Log.e(TAG, "Receiving null prediction results.");
- }
- mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
- mAfterCompute.afterCompute();
- }
- break;
-
- case RESOLVER_RANKER_RESULT_TIMEOUT:
- if (DEBUG) {
- Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services");
- }
- mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
- mAfterCompute.afterCompute();
- break;
+ private LogisticRegressionAppRanker mRanker;
- default:
- super.handleMessage(msg);
- }
- }
- };
-
- public interface AfterCompute {
- public void afterCompute ();
- }
-
- public ResolverComparator(Context context, Intent intent, String referrerPackage,
- AfterCompute afterCompute) {
+ public ResolverComparator(Context context, Intent intent, String referrerPackage) {
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
String scheme = intent.getScheme();
mHttp = "http".equals(scheme) || "https".equals(scheme);
mReferrerPackage = referrerPackage;
- mAfterCompute = afterCompute;
- mContext = context;
mPm = context.getPackageManager();
mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
@@ -170,9 +96,9 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
mContentType = intent.getType();
getContentAnnotations(intent);
mAction = intent.getAction();
+ mRanker = new LogisticRegressionAppRanker(context);
}
- // get annotations of content from intent.
public void getContentAnnotations(Intent intent) {
ArrayList<String> annotations = intent.getStringArrayListExtra(
Intent.EXTRA_CONTENT_ANNOTATIONS);
@@ -188,24 +114,20 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
}
}
- public void setCallBack(AfterCompute afterCompute) {
- mAfterCompute = afterCompute;
- }
-
- // compute features for each target according to usage stats of targets.
public void compute(List<ResolvedComponentInfo> targets) {
- reset();
+ mScoredTargets.clear();
final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
- float mostRecencyScore = 1.0f;
- float mostTimeSpentScore = 1.0f;
- float mostLaunchScore = 1.0f;
- float mostChooserScore = 1.0f;
+ long mostRecentlyUsedTime = recentSinceTime + 1;
+ long mostTimeSpent = 1;
+ int mostLaunched = 1;
+ int mostSelected = 1;
for (ResolvedComponentInfo target : targets) {
- final ResolverTarget resolverTarget = new ResolverTarget();
- mTargetsDict.put(target.name, resolverTarget);
+ final ScoredTarget scoredTarget
+ = new ScoredTarget(target.getResolveInfoAt(0).activityInfo);
+ mScoredTargets.put(target.name, scoredTarget);
final UsageStats pkStats = mStats.get(target.name.getPackageName());
if (pkStats != null) {
// Only count recency for apps that weren't the caller
@@ -213,33 +135,31 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
// Persistent processes muck this up, so omit them too.
if (!target.name.getPackageName().equals(mReferrerPackage)
&& !isPersistentProcess(target)) {
- final float recencyScore =
- (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0);
- resolverTarget.setRecencyScore(recencyScore);
- if (recencyScore > mostRecencyScore) {
- mostRecencyScore = recencyScore;
+ final long lastTimeUsed = pkStats.getLastTimeUsed();
+ scoredTarget.lastTimeUsed = lastTimeUsed;
+ if (lastTimeUsed > mostRecentlyUsedTime) {
+ mostRecentlyUsedTime = lastTimeUsed;
}
}
- final float timeSpentScore = (float) pkStats.getTotalTimeInForeground();
- resolverTarget.setTimeSpentScore(timeSpentScore);
- if (timeSpentScore > mostTimeSpentScore) {
- mostTimeSpentScore = timeSpentScore;
+ final long timeSpent = pkStats.getTotalTimeInForeground();
+ scoredTarget.timeSpent = timeSpent;
+ if (timeSpent > mostTimeSpent) {
+ mostTimeSpent = timeSpent;
}
- final float launchScore = (float) pkStats.mLaunchCount;
- resolverTarget.setLaunchScore(launchScore);
- if (launchScore > mostLaunchScore) {
- mostLaunchScore = launchScore;
+ final int launched = pkStats.mLaunchCount;
+ scoredTarget.launchCount = launched;
+ if (launched > mostLaunched) {
+ mostLaunched = launched;
}
- float chooserScore = 0.0f;
+ int selected = 0;
if (pkStats.mChooserCounts != null && mAction != null
&& pkStats.mChooserCounts.get(mAction) != null) {
- chooserScore = (float) pkStats.mChooserCounts.get(mAction)
- .getOrDefault(mContentType, 0);
+ selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
if (mAnnotations != null) {
final int size = mAnnotations.length;
for (int i = 0; i < size; i++) {
- chooserScore += (float) pkStats.mChooserCounts.get(mAction)
+ selected += pkStats.mChooserCounts.get(mAction)
.getOrDefault(mAnnotations[i], 0);
}
}
@@ -249,37 +169,44 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
Log.d(TAG, "Action type is null");
} else {
Log.d(TAG, "Chooser Count of " + mAction + ":" +
- target.name.getPackageName() + " is " +
- Float.toString(chooserScore));
+ target.name.getPackageName() + " is " + Integer.toString(selected));
}
}
- resolverTarget.setChooserScore(chooserScore);
- if (chooserScore > mostChooserScore) {
- mostChooserScore = chooserScore;
+ scoredTarget.chooserCount = selected;
+ if (selected > mostSelected) {
+ mostSelected = selected;
}
}
}
+
if (DEBUG) {
- Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore
- + " mostTimeSpentScore: " + mostTimeSpentScore
- + " mostLaunchScore: " + mostLaunchScore
- + " mostChooserScore: " + mostChooserScore);
+ Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime
+ + " mostTimeSpent: " + mostTimeSpent
+ + " recentSinceTime: " + recentSinceTime
+ + " mostLaunched: " + mostLaunched);
}
- mTargets = new ArrayList<>(mTargetsDict.values());
- for (ResolverTarget target : mTargets) {
- final float recency = target.getRecencyScore() / mostRecencyScore;
- setFeatures(target, recency * recency * RECENCY_MULTIPLIER,
- target.getLaunchScore() / mostLaunchScore,
- target.getTimeSpentScore() / mostTimeSpentScore,
- target.getChooserScore() / mostChooserScore);
- addDefaultSelectProbability(target);
+ for (ScoredTarget target : mScoredTargets.values()) {
+ final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
+ / (mostRecentlyUsedTime - recentSinceTime);
+ target.setFeatures((float) target.launchCount / mostLaunched,
+ (float) target.timeSpent / mostTimeSpent,
+ recency * recency * RECENCY_MULTIPLIER,
+ (float) target.chooserCount / mostSelected);
+ target.selectProb = mRanker.predict(target.getFeatures());
if (DEBUG) {
Log.d(TAG, "Scores: " + target);
}
}
- predictSelectProbabilities(mTargets);
+ }
+
+ static boolean isPersistentProcess(ResolvedComponentInfo rci) {
+ if (rci != null && rci.getCount() > 0) {
+ return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
+ ApplicationInfo.FLAG_PERSISTENT) != 0;
+ }
+ return false;
}
@Override
@@ -318,16 +245,16 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
// Pinned items stay stable within a normal lexical sort and ignore scoring.
if (!lPinned && !rPinned) {
if (mStats != null) {
- final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
+ final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName(
lhs.activityInfo.packageName, lhs.activityInfo.name));
- final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
+ final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
rhs.activityInfo.packageName, rhs.activityInfo.name));
- final int selectProbabilityDiff = Float.compare(
- rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
+ final int selectProbDiff = Float.compare(
+ rhsTarget.selectProb, lhsTarget.selectProb);
- if (selectProbabilityDiff != 0) {
- return selectProbabilityDiff > 0 ? 1 : -1;
+ if (selectProbDiff != 0) {
+ return selectProbDiff > 0 ? 1 : -1;
}
}
}
@@ -341,234 +268,177 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
}
public float getScore(ComponentName name) {
- final ResolverTarget target = mTargetsDict.get(name);
+ final ScoredTarget target = mScoredTargets.get(name);
if (target != null) {
- return target.getSelectProbability();
+ return target.selectProb;
}
return 0;
}
- public void updateChooserCounts(String packageName, int userId, String action) {
- if (mUsm != null) {
- mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
+ static class ScoredTarget {
+ public final ComponentInfo componentInfo;
+ public long lastTimeUsed;
+ public long timeSpent;
+ public long launchCount;
+ public long chooserCount;
+ public ArrayMap<String, Float> features;
+ public float selectProb;
+
+ public ScoredTarget(ComponentInfo ci) {
+ componentInfo = ci;
+ features = new ArrayMap<>(5);
}
- }
- // update ranking model when the connection to it is valid.
- public void updateModel(ComponentName componentName) {
- synchronized (mLock) {
- if (mRanker != null) {
- try {
- int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
- .indexOf(componentName);
- if (selectedPos > 0) {
- mRanker.train(mTargets, selectedPos);
- } else {
- if (DEBUG) {
- Log.d(TAG, "Selected a unknown component: " + componentName);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error in Train: " + e);
- }
- } else {
- if (DEBUG) {
- Log.d(TAG, "Ranker is null; skip updateModel.");
- }
- }
+ @Override
+ public String toString() {
+ return "ScoredTarget{" + componentInfo
+ + " lastTimeUsed: " + lastTimeUsed
+ + " timeSpent: " + timeSpent
+ + " launchCount: " + launchCount
+ + " chooserCount: " + chooserCount
+ + " selectProb: " + selectProb
+ + "}";
}
- }
- // unbind the service and clear unhandled messges.
- public void destroy() {
- mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
- mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- mConnection.destroy();
+ public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore,
+ float chooserCountScore) {
+ features.put(LAUNCH_SCORE, launchCountScore);
+ features.put(TIME_SPENT_SCORE, usageTimeScore);
+ features.put(RECENCY_SCORE, recencyScore);
+ features.put(CHOOSER_SCORE, chooserCountScore);
}
- if (DEBUG) {
- Log.d(TAG, "Unbinded Resolver Ranker.");
+
+ public ArrayMap<String, Float> getFeatures() {
+ return features;
}
}
- // connect to a ranking service.
- private void initRanker(Context context) {
- synchronized (mLock) {
- if (mConnection != null && mRanker != null) {
- if (DEBUG) {
- Log.d(TAG, "Ranker still exists; reusing the existing one.");
- }
- return;
- }
- }
- Intent intent = resolveRankerService();
- if (intent == null) {
- return;
+ public void updateChooserCounts(String packageName, int userId, String action) {
+ if (mUsm != null) {
+ mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
}
- mConnectSignal = new CountDownLatch(1);
- mConnection = new ResolverRankerServiceConnection(mConnectSignal);
- context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
}
- // resolve the service for ranking.
- private Intent resolveRankerService() {
- Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE);
- final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0);
- for (ResolveInfo resolveInfo : resolveInfos) {
- if (resolveInfo == null || resolveInfo.serviceInfo == null
- || resolveInfo.serviceInfo.applicationInfo == null) {
- if (DEBUG) {
- Log.d(TAG, "Failed to retrieve a ranker: " + resolveInfo);
- }
- continue;
- }
- ComponentName componentName = new ComponentName(
- resolveInfo.serviceInfo.applicationInfo.packageName,
- resolveInfo.serviceInfo.name);
- try {
- final String perm = mPm.getServiceInfo(componentName, 0).permission;
- if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) {
- Log.w(TAG, "ResolverRankerService " + componentName + " does not require"
- + " permission " + ResolverRankerService.BIND_PERMISSION
- + " - this service will not be queried for ResolverComparator."
- + " add android:permission=\""
- + ResolverRankerService.BIND_PERMISSION + "\""
- + " to the <service> tag for " + componentName
- + " in the manifest.");
- continue;
- }
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Could not look up service " + componentName
- + "; component name not found");
+ public void updateModel(ComponentName componentName) {
+ if (mScoredTargets == null || componentName == null ||
+ !mScoredTargets.containsKey(componentName)) {
+ return;
+ }
+ ScoredTarget selected = mScoredTargets.get(componentName);
+ for (ComponentName targetComponent : mScoredTargets.keySet()) {
+ if (targetComponent.equals(componentName)) {
continue;
}
- if (DEBUG) {
- Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName);
+ ScoredTarget target = mScoredTargets.get(targetComponent);
+ // A potential point of optimization. Save updates or derive a closed form for the
+ // positive case, to avoid calculating them repeatedly.
+ if (target.selectProb >= selected.selectProb) {
+ mRanker.update(target.getFeatures(), target.selectProb, false);
+ mRanker.update(selected.getFeatures(), selected.selectProb, true);
}
- intent.setComponent(componentName);
- return intent;
}
- return null;
+ mRanker.commitUpdate();
}
- // set a watchdog, to avoid waiting for ranking service for too long.
- private void startWatchDog(int timeOutLimit) {
- if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms");
- if (mHandler == null) {
- Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
- }
- mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit);
- }
+ class LogisticRegressionAppRanker {
+ private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
+ private static final String BIAS_PREF_KEY = "bias";
+ private static final String VERSION_PREF_KEY = "version";
- private class ResolverRankerServiceConnection implements ServiceConnection {
- private final CountDownLatch mConnectSignal;
+ // parameters for a pre-trained model, to initialize the app ranker. When updating the
+ // pre-trained model, please update these params, as well as initModel().
+ private static final int CURRENT_VERSION = 1;
+ private static final float LEARNING_RATE = 0.0001f;
+ private static final float REGULARIZER_PARAM = 0.0001f;
- public ResolverRankerServiceConnection(CountDownLatch connectSignal) {
- mConnectSignal = connectSignal;
- }
+ private SharedPreferences mParamSharedPref;
+ private ArrayMap<String, Float> mFeatureWeights;
+ private float mBias;
- public final IResolverRankerResult resolverRankerResult =
- new IResolverRankerResult.Stub() {
- @Override
- public void sendResult(List<ResolverTarget> targets) throws RemoteException {
- if (DEBUG) {
- Log.d(TAG, "Sending Result back to Resolver: " + targets);
- }
- synchronized (mLock) {
- final Message msg = Message.obtain();
- msg.what = RESOLVER_RANKER_SERVICE_RESULT;
- msg.obj = targets;
- mHandler.sendMessage(msg);
- }
- }
- };
+ public LogisticRegressionAppRanker(Context context) {
+ mParamSharedPref = getParamSharedPref(context);
+ initModel();
+ }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) {
- Log.d(TAG, "onServiceConnected: " + name);
+ public float predict(ArrayMap<String, Float> target) {
+ if (target == null) {
+ return 0.0f;
}
- synchronized (mLock) {
- mRanker = IResolverRankerService.Stub.asInterface(service);
- mConnectSignal.countDown();
+ final int featureSize = target.size();
+ float sum = 0.0f;
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ sum += weight * target.valueAt(i);
}
+ return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
}
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) {
- Log.d(TAG, "onServiceDisconnected: " + name);
+ public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
+ if (target == null) {
+ return;
}
- synchronized (mLock) {
- destroy();
+ final int featureSize = target.size();
+ float error = isSelected ? 1.0f - predict : -predict;
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ mBias += LEARNING_RATE * error;
+ currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
+ LEARNING_RATE * error * target.valueAt(i);
+ mFeatureWeights.put(featureName, currentWeight);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
}
}
- public void destroy() {
- synchronized (mLock) {
- mRanker = null;
+ public void commitUpdate() {
+ SharedPreferences.Editor editor = mParamSharedPref.edit();
+ editor.putFloat(BIAS_PREF_KEY, mBias);
+ final int size = mFeatureWeights.size();
+ for (int i = 0; i < size; i++) {
+ editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
}
+ editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
+ editor.apply();
}
- }
- private void reset() {
- mTargetsDict.clear();
- mTargets = null;
- startWatchDog(WATCHDOG_TIMEOUT_MILLIS);
- initRanker(mContext);
- }
-
- // predict select probabilities if ranking service is valid.
- private void predictSelectProbabilities(List<ResolverTarget> targets) {
- if (mConnection == null) {
+ private SharedPreferences getParamSharedPref(Context context) {
+ // The package info in the context isn't initialized in the way it is for normal apps,
+ // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+ // build the path manually below using the same policy that appears in ContextImpl.
if (DEBUG) {
- Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction");
- }
- return;
- } else {
- try {
- mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- synchronized (mLock) {
- if (mRanker != null) {
- mRanker.predict(targets, mConnection.resolverRankerResult);
- return;
- } else {
- if (DEBUG) {
- Log.d(TAG, "Ranker has not been initialized; skip predict.");
- }
- }
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Error in Wait for Service Connection.");
- } catch (RemoteException e) {
- Log.e(TAG, "Error in Predict: " + e);
+ Log.d(TAG, "Context Package Name: " + context.getPackageName());
}
+ final File prefsFile = new File(new File(
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+ context.getUserId(), context.getPackageName()),
+ "shared_prefs"),
+ PARAM_SHARED_PREF_NAME + ".xml");
+ return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
}
- mAfterCompute.afterCompute();
- }
- // adds select prob as the default values, according to a pre-trained Logistic Regression model.
- private void addDefaultSelectProbability(ResolverTarget target) {
- float sum = 2.5543f * target.getLaunchScore() + 2.8412f * target.getTimeSpentScore() +
- 0.269f * target.getRecencyScore() + 4.2222f * target.getChooserScore();
- target.setSelectProbability((float) (1.0 / (1.0 + Math.exp(1.6568f - sum))));
- }
-
- // sets features for each target
- private void setFeatures(ResolverTarget target, float recencyScore, float launchScore,
- float timeSpentScore, float chooserScore) {
- target.setRecencyScore(recencyScore);
- target.setLaunchScore(launchScore);
- target.setTimeSpentScore(timeSpentScore);
- target.setChooserScore(chooserScore);
- }
-
- static boolean isPersistentProcess(ResolvedComponentInfo rci) {
- if (rci != null && rci.getCount() > 0) {
- return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
- ApplicationInfo.FLAG_PERSISTENT) != 0;
+ private void initModel() {
+ mFeatureWeights = new ArrayMap<>(4);
+ if (mParamSharedPref == null ||
+ mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
+ // Initializing the app ranker to a pre-trained model. When updating the pre-trained
+ // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
+ // REGULARIZER_PARAM.
+ mBias = -1.6568f;
+ mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
+ mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
+ mFeatureWeights.put(RECENCY_SCORE, 0.269f);
+ mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
+ } else {
+ mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+ mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
+ mFeatureWeights.put(
+ TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
+ mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
+ mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
+ }
}
- return false;
}
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index e8bebb74bdd0..4071ff4ebd5a 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -32,10 +32,8 @@ import android.os.RemoteException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import java.lang.InterruptedException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.concurrent.CountDownLatch;
import java.util.List;
/**
@@ -207,42 +205,14 @@ public class ResolverListController {
return listToReturn;
}
- private class ComputeCallback implements ResolverComparator.AfterCompute {
-
- private CountDownLatch mFinishComputeSignal;
-
- public ComputeCallback(CountDownLatch finishComputeSignal) {
- mFinishComputeSignal = finishComputeSignal;
- }
-
- public void afterCompute () {
- mFinishComputeSignal.countDown();
- }
- }
-
@VisibleForTesting
@WorkerThread
public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
- final CountDownLatch finishComputeSignal = new CountDownLatch(1);
- ComputeCallback callback = new ComputeCallback(finishComputeSignal);
if (mResolverComparator == null) {
- mResolverComparator =
- new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, callback);
- } else {
- mResolverComparator.setCallBack(callback);
- }
- try {
- long beforeRank = System.currentTimeMillis();
- mResolverComparator.compute(inputList);
- finishComputeSignal.await();
- Collections.sort(inputList, mResolverComparator);
- long afterRank = System.currentTimeMillis();
- if (DEBUG) {
- Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank));
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Compute & Sort was interrupted: " + e);
+ mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
}
+ mResolverComparator.compute(inputList);
+ Collections.sort(inputList, mResolverComparator);
}
private static boolean isSameResolvedComponent(ResolveInfo a,
@@ -263,7 +233,7 @@ public class ResolverListController {
@VisibleForTesting
public float getScore(ResolverActivity.DisplayResolveInfo target) {
if (mResolverComparator == null) {
- return 0.0f;
+ mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
}
return mResolverComparator.getScore(target.getResolvedComponentName());
}
@@ -279,10 +249,4 @@ public class ResolverListController {
mResolverComparator.updateChooserCounts(packageName, userId, action);
}
}
-
- public void destroy() {
- if (mResolverComparator != null) {
- mResolverComparator.destroy();
- }
- }
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index f987a9f45e9f..caf35b39b324 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -55,8 +55,8 @@ interface IAppWidgetService {
in RemoteViews views);
void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
void notifyAppWidgetViewDataChanged(String packageName, in int[] appWidgetIds, int viewId);
- ParceledListSlice getInstalledProvidersForProfile(int categoryFilter,
- int profileId);
+ ParceledListSlice getInstalledProvidersForProfile(int categoryFilter, int profileId,
+ String packageName);
AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId);
boolean hasBindAppWidgetPermission(in String packageName, int userId);
void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a73f54354062..792225083e8d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3130,15 +3130,6 @@
<permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
android:protectionLevel="signature" />
- <!-- @SystemApi Must be required by services that extend
- {@link android.service.resolver.ResolverRankerService}, to ensure that only the system can
- bind to them.
- <p>Protection level: signature
- @hide
- -->
- <permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE"
- android:protectionLevel="signature" />
-
<!-- Must be required by a {@link
android.service.notification.ConditionProviderService},
to ensure that only the system can bind to it.
@@ -3650,14 +3641,6 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
- <service android:name="com.android.internal.app.LRResolverRankerService"
- android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"
- android:exported="false"
- android:priority="-1" >
- <intent-filter>
- <action android:name="android.service.resolver.ResolverRankerService" />
- </intent-filter>
- </service>
</application>
</manifest>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index a165621b9696..5a2bf4eb3f62 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,27 +17,24 @@
<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:theme="@style/Theme.Material.Notification"
android:id="@+id/notification_header"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="@dimen/notification_header_height"
android:clipChildren="false"
- android:paddingTop="@dimen/notification_header_padding_top"
- android:paddingBottom="@dimen/notification_header_padding_bottom"
- android:layout_marginBottom="5dp"
- android:paddingStart="@dimen/notification_content_margin_start"
- android:paddingEnd="16dp">
+ style="?attr/notificationHeaderStyle">
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
- android:layout_width="@dimen/notification_header_icon_size"
- android:layout_height="@dimen/notification_header_icon_size"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
android:layout_marginEnd="3dp"
/>
<TextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
android:layout_marginStart="3dp"
android:layout_marginEnd="2dp"
android:singleLine="true"
@@ -46,7 +43,7 @@
android:id="@+id/header_text_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:text="@string/notification_header_divider_symbol"
@@ -55,7 +52,7 @@
android:id="@+id/header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:visibility="gone"
@@ -64,7 +61,7 @@
android:id="@+id/time_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:text="@string/notification_header_divider_symbol"
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index f3aa0485bbd2..026bc6e5cb43 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -23,7 +23,8 @@
android:paddingStart="@dimen/notification_extra_margin_ambient"
android:paddingEnd="@dimen/notification_extra_margin_ambient"
>
- <include layout="@layout/notification_template_header" />
+ <include layout="@layout/notification_template_header"
+ android:theme="@style/Theme.Material.Notification.Ambient" />
<LinearLayout
android:id="@+id/notification_action_list_margin_target"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ee73b6983888..d26d952224f1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7739,13 +7739,6 @@
<attr name="settingsActivity" />
</declare-styleable>
- <!-- TODO(b/35956626): temporary until clients change to AutofillService -->
- <declare-styleable name="AutoFillService">
- <!-- Fully qualified class name of an activity that allows the user to modify
- the settings for this service. -->
- <attr name="settingsActivity" />
- </declare-styleable>
-
<!-- =============================== -->
<!-- Contacts meta-data attributes -->
<!-- =============================== -->
@@ -8635,5 +8628,12 @@
<attr name="stackFromEnd" format="boolean" />
</declare-styleable>
+ <!-- @hide -->
+ <declare-styleable name="NotificationTheme">
+ <attr name="notificationHeaderStyle" format="reference" />
+ <attr name="notificationHeaderTextAppearance" format="reference" />
+ <attr name="notificationHeaderIconSize" format="dimension" />
+ </declare-styleable>
+
<attr name="lockPatternStyle" format="reference" />
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b82542af521f..c5316c6133cc 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -191,6 +191,9 @@
<!-- size (width and height) of the icon in the notification header -->
<dimen name="notification_header_icon_size">18dp</dimen>
+ <!-- size (width and height) of the icon in the notification header -->
+ <dimen name="notification_header_icon_size_ambient">20dp</dimen>
+
<!-- Height of a small notification in the status bar -->
<dimen name="notification_min_height">92dp</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 8f061a3862a3..ec1661176ba6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -488,6 +488,10 @@ please see styles_device_defaults.xml.
<style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" />
+ <style name="TextAppearance.Material.Notification.Info.Ambient">
+ <item name="textSize">@dimen/notification_text_size</item>
+ </style>
+
<style name="TextAppearance.Material.Notification.Emphasis">
<item name="textColor">#66000000</item>
</style>
@@ -1283,4 +1287,12 @@ please see styles_device_defaults.xml.
<style name="DialogWindowTitle.Material.Light" />
+ <style name="Notification.Header" parent="">
+ <item name="paddingTop">@dimen/notification_header_padding_top</item>
+ <item name="paddingBottom">@dimen/notification_header_padding_bottom</item>
+ <item name="layout_marginBottom">5dp</item>
+ <item name="paddingStart">@dimen/notification_content_margin_start</item>
+ <item name="paddingEnd">16dp</item>
+ </style>
+
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 008c817dd73c..9dafa7a02849 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1321,6 +1321,19 @@ please see themes_device_defaults.xml.
<item name="windowNoTitle">true</item>
</style>
+ <!-- Theme for inflating notifications -->
+ <style name="Theme.Material.Notification" parent="">
+ <item name="notificationHeaderStyle">@style/Notification.Header</item>
+ <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info</item>
+ <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item>
+ </style>
+
+ <!-- Theme for inflating ambient notification -->
+ <style name="Theme.Material.Notification.Ambient">
+ <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item>
+ <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item>
+ </style>
+
<!-- Default theme for Settings and activities launched from Settings. -->
<style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
<item name="colorPrimary">@color/primary_material_settings_light</item>
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index f9342b7c5899..fa5c3fff283f 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -6,6 +6,7 @@
<string name="no_data_notification_id">Your mobile data has been deactivated</string>
<string name="portal_notification_detail">Tap to visit the %s website</string>
<string name="no_data_notification_detail">Please contact your service provider %s</string>
+ <string name="mobile_data_status_notification_channel_name">Mobile data status</string>
<string name="action_bar_label">Sign in to mobile network</string>
<string name="ssl_error_warning">The network you&#8217;re trying to join has security issues.</string>
<string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 73ff3a9b5d1e..7fd16019267e 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -16,6 +16,7 @@
package com.android.carrierdefaultapp;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
@@ -35,6 +36,7 @@ public class CarrierActionUtils {
private static final String PORTAL_NOTIFICATION_TAG = "CarrierDefault.Portal.Notification";
private static final String NO_DATA_NOTIFICATION_TAG = "CarrierDefault.NoData.Notification";
+ private static final String NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS = "mobile_data_status";
private static final int PORTAL_NOTIFICATION_ID = 0;
private static final int NO_DATA_NOTIFICATION_ID = 1;
private static boolean ENABLE = true;
@@ -150,9 +152,18 @@ public class CarrierActionUtils {
private static Notification getNotification(Context context, int titleId, int textId,
PendingIntent pendingIntent) {
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+ final NotificationManager notificationManager = context.getSystemService(
+ NotificationManager.class);
final Resources resources = context.getResources();
final Bundle extras = Bundle.forPair(Notification.EXTRA_SUBSTITUTE_APP_NAME,
resources.getString(R.string.android_system_label));
+ /* Creates the notification channel and registers it with NotificationManager. If a channel
+ * with the same ID is already registered, NotificationManager will ignore this call.
+ */
+ notificationManager.createNotificationChannel(new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS,
+ resources.getString(R.string.mobile_data_status_notification_channel_name),
+ NotificationManager.IMPORTANCE_DEFAULT));
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(resources.getString(titleId))
.setContentText(String.format(resources.getString(textId),
@@ -167,7 +178,8 @@ public class CarrierActionUtils {
.setLocalOnly(true)
.setWhen(System.currentTimeMillis())
.setShowWhen(false)
- .setExtras(extras);
+ .setExtras(extras)
+ .setChannel(NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS);
if (pendingIntent != null) {
builder.setContentIntent(pendingIntent);
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
index 40abb6c8c2c4..88f133ce57c2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
@@ -41,8 +41,8 @@ public class PrivateStorageInfo {
long privateTotalBytes = 0;
for (VolumeInfo info : sm.getVolumes()) {
if (info.getType() == VolumeInfo.TYPE_PRIVATE && info.isMountedReadable()) {
- privateTotalBytes += stats.getTotalBytes(info.getFsUuid());
- privateFreeBytes += stats.getFreeBytes(info.getFsUuid());
+ privateTotalBytes += sm.getTotalBytes(stats, info);
+ privateFreeBytes += sm.getFreeBytes(stats, info);
}
}
return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes);
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
index 320494c68faf..11060e6c1a05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
@@ -16,6 +16,7 @@
package com.android.settingslib.deviceinfo;
+import android.app.usage.StorageStatsManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
@@ -46,4 +47,14 @@ public class StorageManagerVolumeProvider implements StorageVolumeProvider {
public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
return mStorageManager.findEmulatedForPrivate(privateVolume);
}
+
+ @Override
+ public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) {
+ return stats.getTotalBytes(volume.getFsUuid());
+ }
+
+ @Override
+ public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) {
+ return stats.getFreeBytes(volume.getFsUuid());
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
index 646c42f05a8e..e5d85d147bee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
@@ -16,6 +16,7 @@
package com.android.settingslib.deviceinfo;
+import android.app.usage.StorageStatsManager;
import android.os.storage.VolumeInfo;
import java.util.List;
@@ -39,4 +40,18 @@ public interface StorageVolumeProvider {
* Returns the emulated volume for a given private volume.
*/
VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume);
+
+ /**
+ * Returns the total bytes for a given storage volume.
+ *
+ * @pre The volume is a private volume and is readable.
+ */
+ long getTotalBytes(StorageStatsManager stats, VolumeInfo volume);
+
+ /**
+ * Returns the free bytes for a given storage volume.
+ *
+ * @pre The volume is a private volume and is readable.
+ */
+ long getFreeBytes(StorageStatsManager stats, VolumeInfo volume);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index a0f2891493e4..8042321ef29f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -23,7 +23,6 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-import com.android.internal.widget.CachingIconView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
@@ -127,12 +126,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
super.setDark(dark, fade, delay);
if (mDark == dark) return;
mDark = dark;
- if (fade) {
- mViewInvertHelper.fade(dark, delay);
- } else {
- mViewInvertHelper.update(dark);
- }
- mShelfIcons.setAmbient(dark);
+ mShelfIcons.setDark(dark, fade, delay);
+ updateInteractiveness();
}
@Override
@@ -576,7 +571,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
private void updateInteractiveness() {
- mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf;
+ mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf
+ && !mDark;
setClickable(mInteractive);
setFocusable(mInteractive);
setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index b9c8a784e779..92bfae9e3f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,6 +27,7 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -46,6 +47,7 @@ import android.view.animation.Interpolator;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import java.text.NumberFormat;
@@ -99,7 +101,6 @@ public class StatusBarIconView extends AnimatedImageView {
private int mDensity;
private float mIconScale = 1.0f;
private final Paint mDotPaint = new Paint();
- private boolean mDotVisible;
private float mDotRadius;
private int mStaticDotRadius;
private int mVisibleState = STATE_ICON;
@@ -110,6 +111,8 @@ public class StatusBarIconView extends AnimatedImageView {
private OnVisibilityChangedListener mOnVisibilityChangedListener;
private int mDrawableColor;
private int mIconColor;
+ private int mDecorColor;
+ private float mDarkAmount;
private ValueAnimator mColorAnimator;
private int mCurrentSetColor = NO_COLOR;
private int mAnimationStartColor = NO_COLOR;
@@ -119,6 +122,7 @@ public class StatusBarIconView extends AnimatedImageView {
animation.getAnimatedFraction());
setColorInternal(newColor);
};
+ private final NotificationIconDozeHelper mDozer;
public StatusBarIconView(Context context, String slot, Notification notification) {
this(context, slot, notification, false);
@@ -127,6 +131,7 @@ public class StatusBarIconView extends AnimatedImageView {
public StatusBarIconView(Context context, String slot, Notification notification,
boolean blocked) {
super(context);
+ mDozer = new NotificationIconDozeHelper(context);
mBlocked = blocked;
mSlot = slot;
mNumberPain = new Paint();
@@ -190,6 +195,7 @@ public class StatusBarIconView extends AnimatedImageView {
public StatusBarIconView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mDozer = new NotificationIconDozeHelper(context);
mBlocked = false;
mAlwaysScaleIcon = true;
updateIconScale();
@@ -466,7 +472,19 @@ public class StatusBarIconView extends AnimatedImageView {
* to the drawable.
*/
public void setDecorColor(int iconTint) {
- mDotPaint.setColor(iconTint);
+ mDecorColor = iconTint;
+ updateDecorColor();
+ }
+
+ private void updateDecorColor() {
+ int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount);
+ if (mDotPaint.getColor() != color) {
+ mDotPaint.setColor(color);
+
+ if (mDotAppearAmount != 0) {
+ invalidate();
+ }
+ }
}
/**
@@ -477,6 +495,7 @@ public class StatusBarIconView extends AnimatedImageView {
mDrawableColor = color;
setColorInternal(color);
mIconColor = color;
+ mDozer.setColor(color);
}
private void setColorInternal(int color) {
@@ -649,6 +668,14 @@ public class StatusBarIconView extends AnimatedImageView {
mOnVisibilityChangedListener = listener;
}
+ public void setDark(boolean dark, boolean fade, long delay) {
+ mDozer.setImageDark(this, dark, fade, delay, mIconColor == NO_COLOR);
+ mDozer.setIntensityDark(f -> {
+ mDarkAmount = f;
+ updateDecorColor();
+ }, dark, fade, delay);
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 3efa29f87450..bca4b43afc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.content.Context;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.view.View;
@@ -38,8 +38,8 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
private boolean mIsLegacy;
private int mLegacyColor;
- protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) {
- super(view, row);
+ protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+ super(ctx, view, row);
mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color);
}
@@ -67,13 +67,11 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
}
protected void fadeGrayscale(final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateGrayscaleMatrix((float) animation.getAnimatedValue());
- mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- mView.setLayerPaint(mGreyPaint);
- }
+ getDozer().startIntensityAnimation(animation -> {
+ getDozer().updateGrayscaleMatrix((float) animation.getAnimatedValue());
+ mGreyPaint.setColorFilter(
+ new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix()));
+ mView.setLayerPaint(mGreyPaint);
}, dark, delay, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -86,9 +84,9 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
protected void updateGrayscale(boolean dark) {
if (dark) {
- updateGrayscaleMatrix(1f);
+ getDozer().updateGrayscaleMatrix(1f);
mGreyPaint.setColorFilter(
- new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix()));
mView.setLayerPaint(mGreyPaint);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
new file mode 100644
index 000000000000..d592c5f5b7f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar.notification;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.widget.ImageView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+import java.util.function.Consumer;
+
+public class NotificationDozeHelper {
+ private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+
+ public void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+ startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateGrayscaleMatrix((float) animation.getAnimatedValue());
+ target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ }
+ }, dark, delay, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!dark) {
+ target.setColorFilter(null);
+ }
+ }
+ });
+ }
+
+ public void updateGrayscale(ImageView target, boolean dark) {
+ if (dark) {
+ updateGrayscaleMatrix(1f);
+ target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ } else {
+ target.setColorFilter(null);
+ }
+ }
+
+ public void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+ boolean dark, long delay, Animator.AnimatorListener listener) {
+ float startIntensity = dark ? 0f : 1f;
+ float endIntensity = dark ? 1f : 0f;
+ ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+ animator.addUpdateListener(updateListener);
+ animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ animator.setStartDelay(delay);
+ if (listener != null) {
+ animator.addListener(listener);
+ }
+ animator.start();
+ }
+
+ public void setIntensityDark(Consumer<Float> listener, boolean dark,
+ boolean animate, long delay) {
+ if (animate) {
+ startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dark, delay,
+ null /* listener */);
+ } else {
+ listener.accept(dark ? 1f : 0f);
+ }
+ }
+
+ public void updateGrayscaleMatrix(float intensity) {
+ mGrayscaleColorMatrix.setSaturation(1 - intensity);
+ }
+
+ public ColorMatrix getGrayscaleColorMatrix() {
+ return mGrayscaleColorMatrix;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 38e4ec1a4d7f..1ffc94480dab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -16,17 +16,10 @@
package com.android.systemui.statusbar.notification;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.app.Notification;
import android.content.Context;
-import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -37,7 +30,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.Interpolators;
-import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
@@ -55,10 +47,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
= new PathInterpolator(0.4f, 0f, 0.7f, 1f);
- private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
- 0, PorterDuff.Mode.SRC_ATOP);
- private final int mIconDarkAlpha;
- private final int mIconDarkColor = 0xffffffff;
protected final ViewInvertHelper mInvertHelper;
protected final ViewTransformationHelper mTransformationHelper;
@@ -74,8 +62,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private boolean mTransformLowPriorityTitle;
protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
- super(view, row);
- mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+ super(ctx, view, row);
mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
mTransformationHelper = new ViewTransformationHelper();
@@ -108,6 +95,16 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
updateInvertHelper();
}
+ @Override
+ protected NotificationDozeHelper createDozer(Context ctx) {
+ return new NotificationIconDozeHelper(ctx);
+ }
+
+ @Override
+ protected NotificationIconDozeHelper getDozer() {
+ return (NotificationIconDozeHelper) super.getDozer();
+ }
+
protected void resolveHeaderViews() {
mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text);
@@ -116,6 +113,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mColor = resolveColor(mExpandButton);
mNotificationHeader = (NotificationHeaderView) mView.findViewById(
com.android.internal.R.id.notification_header);
+ getDozer().setColor(mColor);
}
private int resolveColor(ImageView icon) {
@@ -223,90 +221,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
// It also may lead to bugs where the icon isn't correctly greyed out.
boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
!= NotificationHeaderView.NO_COLOR;
- if (fade) {
- if (hadColorFilter) {
- fadeIconColorFilter(mIcon, dark, delay);
- fadeIconAlpha(mIcon, dark, delay);
- } else {
- fadeGrayscale(mIcon, dark, delay);
- }
- } else {
- if (hadColorFilter) {
- updateIconColorFilter(mIcon, dark);
- updateIconAlpha(mIcon, dark);
- } else {
- updateGrayscale(mIcon, dark);
- }
- }
- }
- }
-
- private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateIconColorFilter(target, (Float) animation.getAnimatedValue());
- }
- }, dark, delay, null /* listener */);
- }
-
- private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (float) animation.getAnimatedValue();
- target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
- }
- }, dark, delay, null /* listener */);
- }
-
- protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateGrayscaleMatrix((float) animation.getAnimatedValue());
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- }
- }, dark, delay, new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!dark) {
- target.setColorFilter(null);
- }
- }
- });
- }
-
- private void updateIconColorFilter(ImageView target, boolean dark) {
- updateIconColorFilter(target, dark ? 1f : 0f);
- }
- private void updateIconColorFilter(ImageView target, float intensity) {
- int color = interpolateColor(mColor, mIconDarkColor, intensity);
- mIconColorFilter.setColor(color);
- Drawable iconDrawable = target.getDrawable();
-
- // Also, the notification might have been modified during the animation, so background
- // might be null here.
- if (iconDrawable != null) {
- Drawable d = iconDrawable.mutate();
- // DrawableContainer ignores the color filter if it's already set, so clear it first to
- // get it set and invalidated properly.
- d.setColorFilter(null);
- d.setColorFilter(mIconColorFilter);
- }
- }
-
- private void updateIconAlpha(ImageView target, boolean dark) {
- target.setImageAlpha(dark ? mIconDarkAlpha : 255);
- }
-
- protected void updateGrayscale(ImageView target, boolean dark) {
- if (dark) {
- updateGrayscaleMatrix(1f);
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- } else {
- target.setColorFilter(null);
+ getDozer().setImageDark(mIcon, dark, fade, delay, !hadColorFilter);
}
}
@@ -316,22 +232,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
- private static int interpolateColor(int source, int target, float t) {
- int aSource = Color.alpha(source);
- int rSource = Color.red(source);
- int gSource = Color.green(source);
- int bSource = Color.blue(source);
- int aTarget = Color.alpha(target);
- int rTarget = Color.red(target);
- int gTarget = Color.green(target);
- int bTarget = Color.blue(target);
- return Color.argb(
- (int) (aSource * (1f - t) + aTarget * t),
- (int) (rSource * (1f - t) + rTarget * t),
- (int) (gSource * (1f - t) + gTarget * t),
- (int) (bSource * (1f - t) + bTarget * t));
- }
-
@Override
public NotificationHeaderView getNotificationHeader() {
return mNotificationHeader;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
new file mode 100644
index 000000000000..9f79ef2491d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar.notification;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class NotificationIconDozeHelper extends NotificationDozeHelper {
+
+ private final int mImageDarkAlpha;
+ private final int mImageDarkColor = 0xffffffff;
+ private final PorterDuffColorFilter mImageColorFilter = new PorterDuffColorFilter(
+ 0, PorterDuff.Mode.SRC_ATOP);
+
+ private int mColor = Color.BLACK;
+
+ public NotificationIconDozeHelper(Context ctx) {
+ mImageDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+ }
+
+ public void setColor(int color) {
+ mColor = color;
+ }
+
+ public void setImageDark(ImageView target, boolean dark, boolean fade, long delay,
+ boolean useGrayscale) {
+ if (fade) {
+ if (!useGrayscale) {
+ fadeImageColorFilter(target, dark, delay);
+ fadeImageAlpha(target, dark, delay);
+ } else {
+ fadeGrayscale(target, dark, delay);
+ }
+ } else {
+ if (!useGrayscale) {
+ updateImageColorFilter(target, dark);
+ updateImageAlpha(target, dark);
+ } else {
+ updateGrayscale(target, dark);
+ }
+ }
+ }
+
+ private void fadeImageColorFilter(final ImageView target, boolean dark, long delay) {
+ startIntensityAnimation(animation -> {
+ updateImageColorFilter(target, (Float) animation.getAnimatedValue());
+ }, dark, delay, null /* listener */);
+ }
+
+ private void fadeImageAlpha(final ImageView target, boolean dark, long delay) {
+ startIntensityAnimation(animation -> {
+ float t = (float) animation.getAnimatedValue();
+ target.setImageAlpha((int) (255 * (1f - t) + mImageDarkAlpha * t));
+ }, dark, delay, null /* listener */);
+ }
+
+ private void updateImageColorFilter(ImageView target, boolean dark) {
+ updateImageColorFilter(target, dark ? 1f : 0f);
+ }
+
+ private void updateImageColorFilter(ImageView target, float intensity) {
+ int color = NotificationUtils.interpolateColors(mColor, mImageDarkColor, intensity);
+ mImageColorFilter.setColor(color);
+ Drawable imageDrawable = target.getDrawable();
+
+ // Also, the notification might have been modified during the animation, so background
+ // might be null here.
+ if (imageDrawable != null) {
+ Drawable d = imageDrawable.mutate();
+ // DrawableContainer ignores the color filter if it's already set, so clear it first to
+ // get it set and invalidated properly.
+ d.setColorFilter(null);
+ d.setColorFilter(mImageColorFilter);
+ }
+ }
+
+ private void updateImageAlpha(ImageView target, boolean dark) {
+ target.setImageAlpha(dark ? mImageDarkAlpha : 255);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 846d03ac74dd..f0b6b2e89e9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.service.notification.StatusBarNotification;
@@ -46,7 +45,8 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
private int mContentHeight;
private int mMinHeightHint;
- protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+ protected NotificationTemplateViewWrapper(Context ctx, View view,
+ ExpandableNotificationRow row) {
super(ctx, view, row);
mTransformationHelper.setCustomTransformation(
new ViewTransformationHelper.CustomTransformation() {
@@ -154,16 +154,20 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
// This also clears the existing types
super.updateTransformedTypes();
if (mTitle != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
+ mTitle);
}
if (mText != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+ mText);
}
if (mPicture != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
+ mPicture);
}
if (mProgressBar != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
+ mProgressBar);
}
}
@@ -173,7 +177,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
return;
}
super.setDark(dark, fade, delay);
- setPictureGrayscale(dark, fade, delay);
+ setPictureDark(dark, fade, delay);
setProgressBarDark(dark, fade, delay);
}
@@ -188,12 +192,9 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
}
private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (float) animation.getAnimatedValue();
- updateProgressDark(target, t);
- }
+ getDozer().startIntensityAnimation(animation -> {
+ float t = (float) animation.getAnimatedValue();
+ updateProgressDark(target, t);
}, dark, delay, null /* listener */);
}
@@ -207,13 +208,9 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
updateProgressDark(target, dark ? 1f : 0f);
}
- protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
+ private void setPictureDark(boolean dark, boolean fade, long delay) {
if (mPicture != null) {
- if (fade) {
- fadeGrayscale(mPicture, grayscale, delay);
- } else {
- updateGrayscale(mPicture, grayscale);
- }
+ getDozer().setImageDark(mPicture, dark, fade, delay, true /* useGrayscale */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index c85e8d853b0d..f4db9a1977f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -16,24 +16,17 @@
package com.android.systemui.statusbar.notification;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
import android.support.v4.graphics.ColorUtils;
import android.view.NotificationHeaderView;
import android.view.View;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Wraps the actual notification content view; used to implement behaviors which are different for
@@ -41,14 +34,14 @@ import com.android.systemui.statusbar.phone.NotificationPanelView;
*/
public abstract class NotificationViewWrapper implements TransformableView {
- protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
protected final View mView;
protected final ExpandableNotificationRow mRow;
+ private final NotificationDozeHelper mDozer;
+
protected boolean mDark;
private int mBackgroundColor = 0;
protected boolean mShouldInvertDark;
protected boolean mDarkInitialized = false;
- private boolean mForcedInvisible;
public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -65,13 +58,22 @@ public abstract class NotificationViewWrapper implements TransformableView {
} else if (v instanceof NotificationHeaderView) {
return new NotificationHeaderViewWrapper(ctx, v, row);
} else {
- return new NotificationCustomViewWrapper(v, row);
+ return new NotificationCustomViewWrapper(ctx, v, row);
}
}
- protected NotificationViewWrapper(View view, ExpandableNotificationRow row) {
+ protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
mView = view;
mRow = row;
+ mDozer = createDozer(ctx);
+ }
+
+ protected NotificationDozeHelper createDozer(Context ctx) {
+ return new NotificationDozeHelper();
+ }
+
+ protected NotificationDozeHelper getDozer() {
+ return mDozer;
}
/**
@@ -112,26 +114,6 @@ public abstract class NotificationViewWrapper implements TransformableView {
|| ColorUtils.calculateLuminance(backgroundColor) > 0.5;
}
-
- protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
- boolean dark, long delay, Animator.AnimatorListener listener) {
- float startIntensity = dark ? 0f : 1f;
- float endIntensity = dark ? 1f : 0f;
- ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
- animator.addUpdateListener(updateListener);
- animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
- animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- animator.setStartDelay(delay);
- if (listener != null) {
- animator.addListener(listener);
- }
- animator.start();
- }
-
- protected void updateGrayscaleMatrix(float intensity) {
- mGrayscaleColorMatrix.setSaturation(1 - intensity);
- }
-
/**
* Update the appearance of the expand button.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 3706dc8242b9..dee15d8163a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -95,7 +95,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private int mActualLayoutWidth = NO_VALUE;
private float mActualPaddingEnd = NO_VALUE;
private float mActualPaddingStart = NO_VALUE;
- private boolean mCentered;
+ private boolean mDark;
private boolean mChangingViewPositions;
private int mAddAnimationStartIndex = -1;
private int mCannedAnimationStartIndex = -1;
@@ -183,6 +183,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
}
}
+ if (mDark && child instanceof StatusBarIconView) {
+ ((StatusBarIconView) child).setDark(mDark, false, 0);
+ }
}
@Override
@@ -312,7 +315,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
numDots++;
}
}
- if (mCentered && translationX < getLayoutEnd()) {
+ boolean center = mDark;
+ if (center && translationX < getLayoutEnd()) {
float delta = (getLayoutEnd() - translationX) / 2;
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
@@ -390,9 +394,15 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mChangingViewPositions = changingViewPositions;
}
- public void setAmbient(boolean ambient) {
- mCentered = ambient;
+ public void setDark(boolean dark, boolean fade, long delay) {
+ mDark = dark;
mDisallowNextAnimation = true;
+ for (int i = 0; i < getChildCount(); i++) {
+ View view = getChildAt(i);
+ if (view instanceof StatusBarIconView) {
+ ((StatusBarIconView) view).setDark(dark, fade, delay);
+ }
+ }
}
public IconState getIconState(StatusBarIconView icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ff4cbfc235b0..472af653af6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5010,23 +5010,25 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (!mHeadsUpManager.getAllEntries().isEmpty()) {
+ Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
+ mHeadsUpManager.getAllEntries();
+ if (!pulsingEntries.isEmpty()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
- setPulsing(true);
+ setPulsing(pulsingEntries);
}
}
@Override
public void onPulseFinished() {
callback.onPulseFinished();
- setPulsing(false);
+ setPulsing(null);
}
- private void setPulsing(boolean pulsing) {
+ private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
mStackScroller.setPulsing(pulsing);
- mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing);
+ mNotificationPanel.setPulsing(pulsing != null);
+ mVisualStabilityManager.setPulsing(pulsing != null);
}
}, reason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 5bead730fcf1..15fcb38ccf6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -72,6 +72,7 @@ import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationGuts;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StackScrollerDecorView;
@@ -86,6 +87,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
@@ -331,7 +333,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- private boolean mPulsing;
+ private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
private boolean mDrawBackgroundAsSrc;
private boolean mFadingOut;
private boolean mParentNotFullyVisible;
@@ -1917,15 +1919,19 @@ public class NotificationStackScrollLayout extends ViewGroup
int numShownItems = 0;
boolean finish = false;
int maxDisplayedNotifications = mAmbientState.isDark()
- ? (mPulsing ? 1 : 0)
+ ? (isPulsing() ? 1 : 0)
: mMaxDisplayedNotifications;
for (int i = 0; i < getChildCount(); i++) {
ExpandableView expandableView = (ExpandableView) getChildAt(i);
if (expandableView.getVisibility() != View.GONE
&& !expandableView.hasNoContentHeight()) {
- if (maxDisplayedNotifications != -1
- && numShownItems >= maxDisplayedNotifications) {
+ boolean limitReached = maxDisplayedNotifications != -1
+ && numShownItems >= maxDisplayedNotifications;
+ boolean notificationOnAmbientThatIsNotPulsing = isPulsing()
+ && expandableView instanceof ExpandableNotificationRow
+ && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
+ if (limitReached || notificationOnAmbientThatIsNotPulsing) {
expandableView = mShelf;
finish = true;
}
@@ -1971,6 +1977,19 @@ public class NotificationStackScrollLayout extends ViewGroup
mAmbientState.setLayoutMaxHeight(mContentHeight);
}
+ private boolean isPulsing(NotificationData.Entry entry) {
+ for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
+ if (e.entry == entry) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isPulsing() {
+ return mPulsing != null;
+ }
+
private void updateScrollability() {
boolean scrollable = getScrollRange() > 0;
if (scrollable != mScrollable) {
@@ -2784,7 +2803,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void updateNotificationAnimationStates() {
- boolean running = mAnimationsEnabled || mPulsing;
+ boolean running = mAnimationsEnabled || isPulsing();
mShelf.setAnimationsEnabled(running);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -2795,7 +2814,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void updateAnimationState(View child) {
- updateAnimationState((mAnimationsEnabled || mPulsing)
+ updateAnimationState((mAnimationsEnabled || isPulsing())
&& (mIsExpanded || isPinnedHeadsUp(child)), child);
}
@@ -4055,12 +4074,12 @@ public class NotificationStackScrollLayout extends ViewGroup
return mIsExpanded;
}
- public void setPulsing(boolean pulsing) {
- if (mPulsing == pulsing) {
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+ if (mPulsing == null && pulsing == null) {
return;
}
mPulsing = pulsing;
- mAmbientState.setPulsing(pulsing);
+ mAmbientState.setPulsing(isPulsing());
updateNotificationAnimationStates();
updateContentHeight();
notifyHeightChangeListener(mShelf);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
new file mode 100644
index 000000000000..a69de7a68291
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar.notification;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class NotificationViewWrapperTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void constructor_doesntUseViewContext() throws Exception {
+ new TestableNotificationViewWrapper(mContext, new View(null /* context */), null /* row */);
+ }
+
+ static class TestableNotificationViewWrapper extends NotificationViewWrapper {
+ protected TestableNotificationViewWrapper(Context ctx, View view,
+ ExpandableNotificationRow row) {
+ super(ctx, view, row);
+ }
+ }
+} \ No newline at end of file
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 0482e734c234..ac81565fbf3c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1631,7 +1631,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
@Override
public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
- int profileId) {
+ int profileId, String packageName) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
@@ -1653,8 +1653,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Provider provider = mProviders.get(i);
AppWidgetProviderInfo info = provider.info;
- // Ignore an invalid provider or one not matching the filter.
- if (provider.zombie || (info.widgetCategory & categoryFilter) == 0) {
+ // Ignore an invalid provider, one not matching the filter,
+ // or one that isn't in the given package, if any.
+ boolean inPackage = packageName == null
+ || provider.id.componentName.getPackageName().equals(packageName);
+ if (provider.zombie || (info.widgetCategory & categoryFilter) == 0 || !inPackage) {
continue;
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e560d325e6dd..738365d5b36f 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -427,14 +427,13 @@ public class AccountManagerService
public boolean addAccountExplicitlyWithVisibility(Account account, String password,
Bundle extras, Map packageToVisibility) {
Bundle.setDefusable(extras, true);
-
- final int callingUid = Binder.getCallingUid();
+ int callingUid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid());
}
Preconditions.checkNotNull(account, "account cannot be null");
- int userId = UserHandle.getCallingUserId();
if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
String msg = String.format("uid %s cannot explicitly add accounts of type: %s",
callingUid, account.type);
@@ -461,9 +460,9 @@ public class AccountManagerService
public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
String accountType) {
int callingUid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
boolean isSystemUid = UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
- List<String> managedTypes =
- getTypesForCaller(callingUid, UserHandle.getUserId(callingUid), isSystemUid);
+ List<String> managedTypes = getTypesForCaller(callingUid, userId, isSystemUid);
if ((accountType != null && !managedTypes.contains(accountType))
|| (accountType == null && !isSystemUid)) {
@@ -478,8 +477,9 @@ public class AccountManagerService
long identityToken = clearCallingIdentity();
try {
+ UserAccounts accounts = getUserAccounts(userId);
return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid,
- getUserAccounts(UserHandle.getUserId(callingUid)));
+ accounts);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -490,12 +490,8 @@ public class AccountManagerService
*/
private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
- int uid = 0;
- try {
- uid = mPackageManager.getPackageUidAsUser(packageName,
- UserHandle.getUserId(callingUid));
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ if (!packageExistsForUser(packageName, accounts.userId)) {
+ Log.d(TAG, "Package not found " + packageName);
return new LinkedHashMap<>();
}
@@ -520,19 +516,26 @@ public class AccountManagerService
public Map<String, Integer> getPackagesAndVisibilityForAccount(Account account) {
Preconditions.checkNotNull(account, "account cannot be null");
int callingUid = Binder.getCallingUid();
- int userId = UserHandle.getUserId(callingUid);
- UserAccounts accounts = getUserAccounts(userId);
+ int userId = UserHandle.getCallingUserId();
if (!isAccountManagedByCaller(account.type, callingUid, userId)
&& !isSystemUid(callingUid)) {
String msg =
String.format("uid %s cannot get secrets for account %s", callingUid, account);
throw new SecurityException(msg);
}
- synchronized (accounts.dbLock) {
- synchronized (accounts.cacheLock) {
- return getPackagesAndVisibilityForAccountLocked(account, accounts);
+
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(userId);
+ synchronized (accounts.dbLock) {
+ synchronized (accounts.cacheLock) {
+ return getPackagesAndVisibilityForAccountLocked(account, accounts);
+ }
}
+ } finally {
+ restoreCallingIdentity(identityToken);
}
+
}
/**
@@ -560,8 +563,8 @@ public class AccountManagerService
Preconditions.checkNotNull(account, "account cannot be null");
Preconditions.checkNotNull(packageName, "packageName cannot be null");
int callingUid = Binder.getCallingUid();
- UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
- if (!isAccountManagedByCaller(account.type, callingUid, accounts.userId)
+ int userId = UserHandle.getCallingUserId();
+ if (!isAccountManagedByCaller(account.type, callingUid, userId)
&& !isSystemUid(callingUid)) {
String msg = String.format(
"uid %s cannot get secrets for accounts of type: %s",
@@ -569,7 +572,13 @@ public class AccountManagerService
account.type);
throw new SecurityException(msg);
}
- return resolveAccountVisibility(account, packageName, accounts);
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(userId);
+ return resolveAccountVisibility(account, packageName, accounts);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
}
/**
@@ -708,8 +717,8 @@ public class AccountManagerService
Preconditions.checkNotNull(account, "account cannot be null");
Preconditions.checkNotNull(packageName, "packageName cannot be null");
int callingUid = Binder.getCallingUid();
- UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
- if (!isAccountManagedByCaller(account.type, callingUid, accounts.userId)
+ int userId = UserHandle.getCallingUserId();
+ if (!isAccountManagedByCaller(account.type, callingUid, userId)
&& !isSystemUid(callingUid)) {
String msg = String.format(
"uid %s cannot get secrets for accounts of type: %s",
@@ -717,8 +726,14 @@ public class AccountManagerService
account.type);
throw new SecurityException(msg);
}
- return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(userId);
+ return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
accounts);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
}
/**
@@ -805,8 +820,15 @@ public class AccountManagerService
public void registerAccountListener(String[] accountTypes, String opPackageName) {
int callingUid = Binder.getCallingUid();
mAppOpsManager.checkPackage(callingUid, opPackageName);
- registerAccountListener(accountTypes, opPackageName,
- getUserAccounts(UserHandle.getUserId(callingUid)));
+
+ int userId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(userId);
+ registerAccountListener(accountTypes, opPackageName, accounts);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
}
private void registerAccountListener(String[] accountTypes, String opPackageName,
@@ -832,7 +854,18 @@ public class AccountManagerService
public void unregisterAccountListener(String[] accountTypes, String opPackageName) {
int callingUid = Binder.getCallingUid();
mAppOpsManager.checkPackage(callingUid, opPackageName);
- UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
+ int userId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(userId);
+ unregisterAccountListener(accountTypes, opPackageName, accounts);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void unregisterAccountListener(String[] accountTypes, String opPackageName,
+ UserAccounts accounts) {
synchronized (accounts.mReceiversForType) {
if (accountTypes == null) {
// null for any type
@@ -903,7 +936,7 @@ public class AccountManagerService
long identityToken = clearCallingIdentity();
try {
mPackageManager.getPackageUidAsUser(packageName, userId);
- return true; // package exist
+ return true;
} finally {
restoreCallingIdentity(identityToken);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4cbfb275fd32..7dd75df5252a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -31,8 +31,10 @@ import java.util.Set;
import android.app.ActivityThread;
import android.app.AppOpsManager;
+import android.app.ServiceStartArgs;
import android.content.IIntentSender;
import android.content.IntentSender;
+import android.content.pm.ParceledListSlice;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -369,7 +371,8 @@ public final class ActiveServices {
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
- return new ComponentName("?", "app is in background");
+ UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
+ return new ComponentName("?", "app is in background uid " + uidRec);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1956,78 +1959,86 @@ public final class ActiveServices {
return;
}
+ ArrayList<ServiceStartArgs> args = new ArrayList<>();
+
while (r.pendingStarts.size() > 0) {
- Exception caughtException = null;
- ServiceRecord.StartItem si = null;
- try {
- si = r.pendingStarts.remove(0);
- if (DEBUG_SERVICE) {
- Slog.v(TAG_SERVICE, "Sending arguments to: "
- + r + " " + r.intent + " args=" + si.intent);
- }
- if (si.intent == null && N > 1) {
- // If somehow we got a dummy null intent in the middle,
- // then skip it. DO NOT skip a null intent when it is
- // the only one in the list -- this is to support the
- // onStartCommand(null) case.
- continue;
- }
- si.deliveredTime = SystemClock.uptimeMillis();
- r.deliveredStarts.add(si);
- si.deliveryCount++;
- if (si.neededGrants != null) {
- mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
- si.getUriPermissionsLocked());
- }
- mAm.grantEphemeralAccessLocked(r.userId, si.intent,
- r.appInfo.uid, UserHandle.getAppId(si.callingId));
- bumpServiceExecutingLocked(r, execInFg, "start");
- if (!oomAdjusted) {
- oomAdjusted = true;
- mAm.updateOomAdjLocked(r.app);
- }
- if (r.fgRequired && !r.fgWaiting) {
- if (!r.isForeground) {
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
- }
- scheduleServiceForegroundTransitionTimeoutLocked(r);
- } else {
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.i(TAG, "Service already foreground; no new timeout: " + r);
- }
- r.fgRequired = false;
+ ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+ if (DEBUG_SERVICE) {
+ Slog.v(TAG_SERVICE, "Sending arguments to: "
+ + r + " " + r.intent + " args=" + si.intent);
+ }
+ if (si.intent == null && N > 1) {
+ // If somehow we got a dummy null intent in the middle,
+ // then skip it. DO NOT skip a null intent when it is
+ // the only one in the list -- this is to support the
+ // onStartCommand(null) case.
+ continue;
+ }
+ si.deliveredTime = SystemClock.uptimeMillis();
+ r.deliveredStarts.add(si);
+ si.deliveryCount++;
+ if (si.neededGrants != null) {
+ mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
+ si.getUriPermissionsLocked());
+ }
+ mAm.grantEphemeralAccessLocked(r.userId, si.intent,
+ r.appInfo.uid, UserHandle.getAppId(si.callingId));
+ bumpServiceExecutingLocked(r, execInFg, "start");
+ if (!oomAdjusted) {
+ oomAdjusted = true;
+ mAm.updateOomAdjLocked(r.app);
+ }
+ if (r.fgRequired && !r.fgWaiting) {
+ if (!r.isForeground) {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
}
+ scheduleServiceForegroundTransitionTimeoutLocked(r);
+ } else {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Service already foreground; no new timeout: " + r);
+ }
+ r.fgRequired = false;
}
- int flags = 0;
- if (si.deliveryCount > 1) {
- flags |= Service.START_FLAG_RETRY;
- }
- if (si.doneExecutingCount > 0) {
- flags |= Service.START_FLAG_REDELIVERY;
- }
- r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
- } catch (TransactionTooLargeException e) {
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
- + si.intent);
- caughtException = e;
- } catch (RemoteException e) {
- // Remote process gone... we'll let the normal cleanup take care of this.
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
- caughtException = e;
- } catch (Exception e) {
- Slog.w(TAG, "Unexpected exception", e);
- caughtException = e;
}
+ int flags = 0;
+ if (si.deliveryCount > 1) {
+ flags |= Service.START_FLAG_RETRY;
+ }
+ if (si.doneExecutingCount > 0) {
+ flags |= Service.START_FLAG_REDELIVERY;
+ }
+ args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
+ }
- if (caughtException != null) {
- // Keep nesting count correct
- final boolean inDestroying = mDestroyingServices.contains(r);
+ ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
+ slice.setInlineCountLimit(4);
+ Exception caughtException = null;
+ try {
+ r.app.thread.scheduleServiceArgs(r, slice);
+ } catch (TransactionTooLargeException e) {
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
+ + " args, first: " + args.get(0).args);
+ Slog.w(TAG, "Failed delivering service starts", e);
+ caughtException = e;
+ } catch (RemoteException e) {
+ // Remote process gone... we'll let the normal cleanup take care of this.
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
+ Slog.w(TAG, "Failed delivering service starts", e);
+ caughtException = e;
+ } catch (Exception e) {
+ Slog.w(TAG, "Unexpected exception", e);
+ caughtException = e;
+ }
+
+ if (caughtException != null) {
+ // Keep nesting count correct
+ final boolean inDestroying = mDestroyingServices.contains(r);
+ for (int i = 0; i < args.size(); i++) {
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
- if (caughtException instanceof TransactionTooLargeException) {
- throw (TransactionTooLargeException)caughtException;
- }
- break;
+ }
+ if (caughtException instanceof TransactionTooLargeException) {
+ throw (TransactionTooLargeException)caughtException;
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19fc2b876c3e..ee2fdba71576 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12143,6 +12143,24 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
+ @Override
+ public void backgroundWhitelistUid(final int uid) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the OS may call backgroundWhitelistUid()");
+ }
+
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
+ }
+ synchronized (this) {
+ final int N = mBackgroundUidWhitelist.length;
+ int[] newList = new int[N+1];
+ System.arraycopy(mBackgroundUidWhitelist, 0, newList, 0, N);
+ newList[N] = uid;
+ mBackgroundUidWhitelist = newList;
+ }
+ }
+
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
ProcessRecord app;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b72cd73e85cf..3e3fee54bdd0 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -745,7 +745,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
/**
- * Returns a {@link TaskRecord} for the input id if available. Null otherwise.
+ * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
* @param id Id of the task we would like returned.
* @param matchMode The mode to match the given task id in.
* @param stackId The stack to restore the task to (default launch stack will be used if
@@ -765,7 +765,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
ActivityStack stack = stacks.get(stackNdx);
- TaskRecord task = stack.taskForIdLocked(id);
+ final TaskRecord task = stack.taskForIdLocked(id);
if (task != null) {
return task;
}
@@ -780,11 +780,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Otherwise, check the recent tasks and return if we find it there and we are not restoring
// the task from recents
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
- TaskRecord task = mRecentTasks.taskForIdLocked(id);
- if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
- if (DEBUG_RECENTS && task == null) {
+ final TaskRecord task = mRecentTasks.taskForIdLocked(id);
+
+ if (task == null) {
+ if (DEBUG_RECENTS) {
Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
}
+
+ return null;
+ }
+
+ if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
return task;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3303081314a5..fe40efb7ef4a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -608,6 +608,10 @@ public class PackageManagerService extends IPackageManager.Stub {
final boolean mIsPreNUpgrade;
final boolean mIsPreNMR1Upgrade;
+ // Have we told the Activity Manager to whitelist the default container service by uid yet?
+ @GuardedBy("mPackages")
+ boolean mDefaultContainerWhitelisted = false;
+
@GuardedBy("mPackages")
private boolean mDexOptDialogShown;
@@ -3057,9 +3061,17 @@ public class PackageManagerService extends IPackageManager.Stub {
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
- final List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
+ List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
-
+ // temporarily look for the old action
+ if (resolvers.size() == 0) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
+ }
+ resolverIntent.setAction(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
+ resolvers = queryIntentServicesInternal(resolverIntent, null,
+ resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
+ }
final int N = resolvers.size();
if (N == 0) {
if (DEBUG_EPHEMERAL) {
@@ -3106,8 +3118,17 @@ public class PackageManagerService extends IPackageManager.Stub {
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+ List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
+ // temporarily look for the old action
+ if (matches.isEmpty()) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral installer not found with new action; try old one");
+ }
+ intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
+ matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+ resolveFlags, UserHandle.USER_SYSTEM);
+ }
Iterator<ResolveInfo> iter = matches.iterator();
while (iter.hasNext()) {
final ResolveInfo rInfo = iter.next();
@@ -3136,8 +3157,17 @@ public class PackageManagerService extends IPackageManager.Stub {
.addCategory(Intent.CATEGORY_DEFAULT)
.setPackage(resolver.getPackageName());
final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
+ List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
UserHandle.USER_SYSTEM);
+ // temporarily look for the old action
+ if (matches.isEmpty()) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one");
+ }
+ intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS);
+ matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
+ UserHandle.USER_SYSTEM);
+ }
if (matches.isEmpty()) {
return null;
}
@@ -13024,7 +13054,18 @@ public class PackageManagerService extends IPackageManager.Stub {
intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
IActivityManager am = ActivityManager.getService();
if (am != null) {
+ int dcsUid = -1;
+ synchronized (mPackages) {
+ if (!mDefaultContainerWhitelisted) {
+ mDefaultContainerWhitelisted = true;
+ PackageSetting ps = mSettings.mPackages.get(DEFAULT_CONTAINER_PACKAGE);
+ dcsUid = UserHandle.getUid(UserHandle.USER_SYSTEM, ps.appId);
+ }
+ }
try {
+ if (dcsUid > 0) {
+ am.backgroundWhitelistUid(dcsUid);
+ }
am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(),
UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index aa374073e4a5..5d0c23f81737 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -149,7 +149,7 @@ public class AccountsDbTest {
// 2nd account
Account account2 = new Account("name", "example2.com");
long accId2 = mAccountsDb.insertCeAccount(account2, "password");
- mAccountsDb.insertDeAccount(account2, accId);
+ mAccountsDb.insertDeAccount(account2, accId2);
mAccountsDb.insertAuthToken(accId2, "type", "token");
mAccountsDb.deleteAuthTokensByAccountId(accId2);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index af3201c7aa97..54ecab3af542 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -33,8 +33,7 @@ import org.junit.Test;
* bit FrameworksServicesTests:com.android.server.am.ActivityRecordTests
*/
@MediumTest
-// TODO(b/36916522): Currently failing in CI.
-// @Presubmit
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
private final ComponentName testActivityComponent =
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
new file mode 100644
index 000000000000..b59c2bc93d73
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.am;
+
+import static org.junit.Assert.assertNull;
+
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStackSupervisorTests extends ActivityTestsBase {
+ /**
+ * This test ensures that we do not try to restore a task based off an invalid task id. The
+ * stack supervisor is a test version so there will be no tasks present. We should expect
+ * {@code null} to be returned in this case.
+ */
+ @Test
+ public void testRestoringInvalidTask() throws Exception {
+ Debug.waitForDebugger();
+ final ActivityManagerService service = createActivityManagerService();
+ TaskRecord task = service.mStackSupervisor.anyTaskForIdLocked(0 /*taskId*/,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/);
+ assertNull(task);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index c5cc2ff22abd..52405863e8fe 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -25,7 +25,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import com.android.server.AttributeCache;
@@ -34,6 +34,7 @@ import com.android.server.wm.StackWindowController;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowTestUtils;
+import org.junit.After;
import org.junit.Before;
import org.mockito.MockitoAnnotations;
@@ -42,8 +43,7 @@ import org.mockito.MockitoAnnotations;
*/
public class ActivityTestsBase {
private final Context mContext = InstrumentationRegistry.getContext();
- private static boolean sLooperPrepared;
- private Handler mHandler;
+ private HandlerThread mHandlerThread;
// Grabbing an instance of {@link WindowManagerService} creates it if not present so this must
// be called at before any tests.
@@ -52,11 +52,13 @@ public class ActivityTestsBase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
+ mHandlerThread.start();
+ }
- if (!sLooperPrepared) {
- sLooperPrepared = true;
- Looper.prepare();
- }
+ @After
+ public void tearDown() {
+ mHandlerThread.quitSafely();
}
protected ActivityManagerService createActivityManagerService() {
@@ -126,7 +128,7 @@ public class ActivityTestsBase {
@Override
protected ActivityStackSupervisor createStackSupervisor() {
- return new TestActivityStackSupervisor(this, new Handler().getLooper());
+ return new TestActivityStackSupervisor(this, mHandlerThread.getLooper());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index f3f68ff06e03..2663aaf35b3d 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -50,6 +50,7 @@ import com.android.server.LocalServices;
import org.mockito.ArgumentCaptor;
+import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
@@ -249,6 +250,25 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
assertEquals(7, updates.size());
}
+ public void testGetInstalledProvidersForPackage() {
+ List<AppWidgetProviderInfo> allProviders = mManager.getInstalledProviders();
+ assertTrue(!allProviders.isEmpty());
+ String packageName = allProviders.get(0).provider.getPackageName();
+ List<AppWidgetProviderInfo> providersForPackage = mManager.getInstalledProvidersForPackage(
+ packageName, null);
+ // Remove providers from allProviders that don't have the given package name.
+ Iterator<AppWidgetProviderInfo> iter = allProviders.iterator();
+ while (iter.hasNext()) {
+ if (!iter.next().provider.getPackageName().equals(packageName)) {
+ iter.remove();
+ }
+ }
+ assertEquals(allProviders.size(), providersForPackage.size());
+ for (int i = 0; i < allProviders.size(); i++) {
+ assertEquals(allProviders.get(i).provider, providersForPackage.get(i).provider);
+ }
+ }
+
private int setupHostAndWidget() {
List<PendingHostUpdate> updates = mService.startListening(
mMockHost, mPkgName, HOST_ID, new int[0]).getList();
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 92233b1fed39..f80ee7386f99 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -356,6 +356,7 @@ public final class Call {
private final StatusHints mStatusHints;
private final Bundle mExtras;
private final Bundle mIntentExtras;
+ private final long mCreationTimeMillis;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -578,9 +579,12 @@ public final class Call {
}
/**
- * @return The time the {@code Call} has been connected. This information is updated
- * periodically, but user interfaces should not rely on this to display any "call time
- * clock".
+ * Returns the time the {@link Call} connected (i.e. became active). This information is
+ * updated periodically, but user interfaces should not rely on this to display the "call
+ * time clock". For the time when the call was first added to Telecom, see
+ * {@link #getCreationTimeMillis()}.
+ *
+ * @return The time the {@link Call} connected in milliseconds since the epoch.
*/
public final long getConnectTimeMillis() {
return mConnectTimeMillis;
@@ -622,6 +626,18 @@ public final class Call {
return mIntentExtras;
}
+ /**
+ * Returns the time when the call was first created and added to Telecom. This is the same
+ * time that is logged as the start time in the Call Log (see
+ * {@link android.provider.CallLog.Calls#DATE}). To determine when the call was connected
+ * (became active), see {@link #getConnectTimeMillis()}.
+ *
+ * @return The creation time of the call, in millis since the epoch.
+ */
+ public long getCreationTimeMillis() {
+ return mCreationTimeMillis;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -641,28 +657,29 @@ public final class Call {
Objects.equals(mVideoState, d.mVideoState) &&
Objects.equals(mStatusHints, d.mStatusHints) &&
areBundlesEqual(mExtras, d.mExtras) &&
- areBundlesEqual(mIntentExtras, d.mIntentExtras);
+ areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
+ Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis);
}
return false;
}
@Override
public int hashCode() {
- return
- Objects.hashCode(mHandle) +
- Objects.hashCode(mHandlePresentation) +
- Objects.hashCode(mCallerDisplayName) +
- Objects.hashCode(mCallerDisplayNamePresentation) +
- Objects.hashCode(mAccountHandle) +
- Objects.hashCode(mCallCapabilities) +
- Objects.hashCode(mCallProperties) +
- Objects.hashCode(mDisconnectCause) +
- Objects.hashCode(mConnectTimeMillis) +
- Objects.hashCode(mGatewayInfo) +
- Objects.hashCode(mVideoState) +
- Objects.hashCode(mStatusHints) +
- Objects.hashCode(mExtras) +
- Objects.hashCode(mIntentExtras);
+ return Objects.hash(mHandle,
+ mHandlePresentation,
+ mCallerDisplayName,
+ mCallerDisplayNamePresentation,
+ mAccountHandle,
+ mCallCapabilities,
+ mCallProperties,
+ mDisconnectCause,
+ mConnectTimeMillis,
+ mGatewayInfo,
+ mVideoState,
+ mStatusHints,
+ mExtras,
+ mIntentExtras,
+ mCreationTimeMillis);
}
/** {@hide} */
@@ -681,7 +698,8 @@ public final class Call {
int videoState,
StatusHints statusHints,
Bundle extras,
- Bundle intentExtras) {
+ Bundle intentExtras,
+ long creationTimeMillis) {
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -697,6 +715,7 @@ public final class Call {
mStatusHints = statusHints;
mExtras = extras;
mIntentExtras = intentExtras;
+ mCreationTimeMillis = creationTimeMillis;
}
/** {@hide} */
@@ -716,7 +735,8 @@ public final class Call {
parcelableCall.getVideoState(),
parcelableCall.getStatusHints(),
parcelableCall.getExtras(),
- parcelableCall.getIntentExtras());
+ parcelableCall.getIntentExtras(),
+ parcelableCall.getCreationTimeMillis());
}
@Override
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 85a92d1a135a..6212a77feba8 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -59,6 +59,7 @@ public final class ParcelableCall implements Parcelable {
private final List<String> mConferenceableCallIds;
private final Bundle mIntentExtras;
private final Bundle mExtras;
+ private final long mCreationTimeMillis;
public ParcelableCall(
String id,
@@ -85,7 +86,8 @@ public final class ParcelableCall implements Parcelable {
int videoState,
List<String> conferenceableCallIds,
Bundle intentExtras,
- Bundle extras) {
+ Bundle extras,
+ long creationTimeMillis) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -111,6 +113,7 @@ public final class ParcelableCall implements Parcelable {
mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
mIntentExtras = intentExtras;
mExtras = extras;
+ mCreationTimeMillis = creationTimeMillis;
}
/** The unique ID of the call. */
@@ -289,6 +292,13 @@ public final class ParcelableCall implements Parcelable {
return mIsVideoCallProviderChanged;
}
+ /**
+ * @return The time the call was created, in milliseconds since the epoch.
+ */
+ public long getCreationTimeMillis() {
+ return mCreationTimeMillis;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
public static final Parcelable.Creator<ParcelableCall> CREATOR =
new Parcelable.Creator<ParcelableCall> () {
@@ -324,6 +334,7 @@ public final class ParcelableCall implements Parcelable {
int supportedAudioRoutes = source.readInt();
boolean isRttCallChanged = source.readByte() == 1;
ParcelableRttCall rttCall = source.readParcelable(classLoader);
+ long creationTimeMillis = source.readLong();
return new ParcelableCall(
id,
state,
@@ -349,7 +360,8 @@ public final class ParcelableCall implements Parcelable {
videoState,
conferenceableCallIds,
intentExtras,
- extras);
+ extras,
+ creationTimeMillis);
}
@Override
@@ -393,6 +405,7 @@ public final class ParcelableCall implements Parcelable {
destination.writeInt(mSupportedAudioRoutes);
destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0));
destination.writeParcelable(mRttCall, 0);
+ destination.writeLong(mCreationTimeMillis);
}
@Override
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 8461905d8034..90f713b67985 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -155,7 +155,10 @@ bool ResourceParser::FlattenXmlSubtree(
xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
std::vector<UntranslatableSection>* out_untranslatable_sections) {
// Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply.
- std::vector<Span> span_stack;
+ // The stack elements refer to the indices in out_style_string->spans.
+ // By first adding to the out_style_string->spans vector, and then using the stack to refer
+ // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>.
+ std::vector<size_t> span_stack;
// Clear the output variables.
out_raw_string->clear();
@@ -192,7 +195,9 @@ bool ResourceParser::FlattenXmlSubtree(
return false;
}
- span_stack.push_back(Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
+ out_style_string->spans.push_back(
+ Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
+ span_stack.push_back(out_style_string->spans.size() - 1);
} else if (parser->element_namespace() == sXliffNamespaceUri) {
if (parser->element_name() == "g") {
if (untranslatable_start_depth) {
@@ -233,9 +238,8 @@ bool ResourceParser::FlattenXmlSubtree(
if (parser->element_namespace().empty()) {
// This is an HTML tag which we encode as a span. Update the span
// stack and pop the top entry.
- Span& top_span = span_stack.back();
+ Span& top_span = out_style_string->spans[span_stack.back()];
top_span.last_char = builder.Utf16Len() - 1;
- out_style_string->spans.push_back(std::move(top_span));
span_stack.pop_back();
} else if (untranslatable_start_depth == make_value(depth)) {
// This is the end of an untranslatable section. Use UTF8 indices/lengths.
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index eefa320a4418..8062c2e6afea 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -101,20 +101,24 @@ TEST_F(ResourceParserTest, ParseStyledString) {
// Use a surrogate pair unicode point so that we can verify that the span
// indices use UTF-16 length and not UTF-8 length.
std::string input =
- "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
+ "<string name=\"foo\">This is my aunt\u2019s <b>fickle <small>string</small></b></string>";
ASSERT_TRUE(TestParse(input));
StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
ASSERT_NE(nullptr, str);
- const std::string expected_str = "This is my aunt\u2019s string";
+ const std::string expected_str = "This is my aunt\u2019s fickle string";
EXPECT_EQ(expected_str, *str->value->str);
- EXPECT_EQ(1u, str->value->spans.size());
+ EXPECT_EQ(2u, str->value->spans.size());
EXPECT_TRUE(str->untranslatable_sections.empty());
EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
EXPECT_EQ(17u, str->value->spans[0].first_char);
- EXPECT_EQ(23u, str->value->spans[0].last_char);
+ EXPECT_EQ(30u, str->value->spans[0].last_char);
+
+ EXPECT_EQ(std::string("small"), *str->value->spans[1].name);
+ EXPECT_EQ(24u, str->value->spans[1].first_char);
+ EXPECT_EQ(30u, str->value->spans[1].last_char);
}
TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index fad9edd04e4c..a031ea4c31ec 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -22,136 +22,194 @@
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "compile/Pseudolocalizer.h"
+#include "util/Util.h"
using android::StringPiece;
+using android::StringPiece16;
namespace aapt {
-std::unique_ptr<StyledString> PseudolocalizeStyledString(
- StyledString* string, Pseudolocalizer::Method method, StringPool* pool) {
- Pseudolocalizer localizer(method);
+// The struct that represents both Span objects and UntranslatableSections.
+struct UnifiedSpan {
+ // Only present for Span objects. If not present, this was an UntranslatableSection.
+ Maybe<std::string> tag;
- const StringPiece original_text = *string->value->str;
+ // The UTF-16 index into the string where this span starts.
+ uint32_t first_char;
- StyleString localized;
+ // The UTF-16 index into the string where this span ends, inclusive.
+ uint32_t last_char;
+};
- // Copy the spans. We will update their offsets when we localize.
- localized.spans.reserve(string->value->spans.size());
- for (const StringPool::Span& span : string->value->spans) {
- localized.spans.push_back(
- Span{*span.name, span.first_char, span.last_char});
+inline static bool operator<(const UnifiedSpan& left, const UnifiedSpan& right) {
+ if (left.first_char < right.first_char) {
+ return true;
+ } else if (left.first_char > right.first_char) {
+ return false;
+ } else if (left.last_char < right.last_char) {
+ return true;
}
+ return false;
+}
- // The ranges are all represented with a single value. This is the start of
- // one range and end of another.
- struct Range {
- size_t start;
-
- // If set to true, toggles the state of translatability.
- bool toggle_translatability;
-
- // Once the new string is localized, these are the pointers to the spans to adjust.
- // Since this struct represents the start of one range and end of another,
- // we have the two pointers respectively.
- uint32_t* update_start;
- uint32_t* update_end;
- };
-
- auto cmp = [](const Range& r, size_t index) -> bool {
- return r.start < index;
- };
-
- // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
- // The ranges are the spaces in between. In this example, with a total string
- // length of 9, the vector represents: (0,1], (2,4], (5,6], (7,9]
- //
- std::vector<Range> ranges;
- ranges.push_back(Range{0, false, nullptr, nullptr});
- ranges.push_back(Range{original_text.size() - 1, false, nullptr, nullptr});
- for (size_t i = 0; i < string->value->spans.size(); i++) {
- const StringPool::Span& span = string->value->spans[i];
-
- // Insert or update the Range marker for the start of this span.
- auto iter =
- std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp);
- if (iter != ranges.end() && iter->start == span.first_char) {
- iter->update_start = &localized.spans[i].first_char;
- } else {
- ranges.insert(iter, Range{span.first_char, false, &localized.spans[i].first_char, nullptr});
- }
+inline static UnifiedSpan SpanToUnifiedSpan(const StringPool::Span& span) {
+ return UnifiedSpan{*span.name, span.first_char, span.last_char};
+}
+
+inline static UnifiedSpan UntranslatableSectionToUnifiedSpan(const UntranslatableSection& section) {
+ return UnifiedSpan{
+ {}, static_cast<uint32_t>(section.start), static_cast<uint32_t>(section.end) - 1};
+}
- // Insert or update the Range marker for the end of this span.
- iter = std::lower_bound(ranges.begin(), ranges.end(), span.last_char, cmp);
- if (iter != ranges.end() && iter->start == span.last_char) {
- iter->update_end = &localized.spans[i].last_char;
+// Merges the Span and UntranslatableSections of this StyledString into a single vector of
+// UnifiedSpans. This will first check that the Spans are sorted in ascending order.
+static std::vector<UnifiedSpan> MergeSpans(const StyledString& string) {
+ // Ensure the Spans are sorted and converted.
+ std::vector<UnifiedSpan> sorted_spans;
+ sorted_spans.reserve(string.value->spans.size());
+ std::transform(string.value->spans.begin(), string.value->spans.end(),
+ std::back_inserter(sorted_spans), SpanToUnifiedSpan);
+
+ // Stable sort to ensure tag sequences like "<b><i>" are preserved.
+ std::stable_sort(sorted_spans.begin(), sorted_spans.end());
+
+ // Ensure the UntranslatableSections are sorted and converted.
+ std::vector<UnifiedSpan> sorted_untranslatable_sections;
+ sorted_untranslatable_sections.reserve(string.untranslatable_sections.size());
+ std::transform(string.untranslatable_sections.begin(), string.untranslatable_sections.end(),
+ std::back_inserter(sorted_untranslatable_sections),
+ UntranslatableSectionToUnifiedSpan);
+ std::sort(sorted_untranslatable_sections.begin(), sorted_untranslatable_sections.end());
+
+ std::vector<UnifiedSpan> merged_spans;
+ merged_spans.reserve(sorted_spans.size() + sorted_untranslatable_sections.size());
+ auto span_iter = sorted_spans.begin();
+ auto untranslatable_iter = sorted_untranslatable_sections.begin();
+ while (span_iter != sorted_spans.end() &&
+ untranslatable_iter != sorted_untranslatable_sections.end()) {
+ if (*span_iter < *untranslatable_iter) {
+ merged_spans.push_back(std::move(*span_iter));
+ ++span_iter;
} else {
- ranges.insert(iter, Range{span.last_char, false, nullptr, &localized.spans[i].last_char});
+ merged_spans.push_back(std::move(*untranslatable_iter));
+ ++untranslatable_iter;
}
}
- // Parts of the string may be untranslatable. Merge those ranges
- // in as well, so that we have continuous sections of text to
- // feed into the pseudolocalizer.
- // We do this by marking the beginning of a range as either toggling
- // the translatability state or not.
- for (const UntranslatableSection& section : string->untranslatable_sections) {
- auto iter = std::lower_bound(ranges.begin(), ranges.end(), section.start, cmp);
- if (iter != ranges.end() && iter->start == section.start) {
- // An existing span starts (or ends) here. We just need to mark that
- // the translatability should toggle here. If translatability was
- // already being toggled, then that means we have two adjacent ranges of untranslatable
- // text, so remove the toggle and only toggle at the end of this range,
- // effectively merging these ranges.
- iter->toggle_translatability = !iter->toggle_translatability;
- } else {
- // Insert a new range that specifies to toggle the translatability.
- iter = ranges.insert(iter, Range{section.start, true, nullptr, nullptr});
- }
+ while (span_iter != sorted_spans.end()) {
+ merged_spans.push_back(std::move(*span_iter));
+ ++span_iter;
+ }
- // Update/create an end to the untranslatable section.
- iter = std::lower_bound(iter, ranges.end(), section.end, cmp);
- if (iter != ranges.end() && iter->start == section.end) {
- iter->toggle_translatability = true;
- } else {
- iter = ranges.insert(iter, Range{section.end, true, nullptr, nullptr});
- }
+ while (untranslatable_iter != sorted_untranslatable_sections.end()) {
+ merged_spans.push_back(std::move(*untranslatable_iter));
+ ++untranslatable_iter;
}
+ return merged_spans;
+}
- localized.str += localizer.Start();
+std::unique_ptr<StyledString> PseudolocalizeStyledString(StyledString* string,
+ Pseudolocalizer::Method method,
+ StringPool* pool) {
+ Pseudolocalizer localizer(method);
- // Iterate over the ranges and localize each section.
- // The text starts as translatable, and each time a range has toggle_translatability
- // set to true, we toggle whether to translate or not.
- // This assumes no untranslatable ranges overlap.
- bool translatable = true;
- for (size_t i = 0; i < ranges.size(); i++) {
- const size_t start = ranges[i].start;
- size_t len = original_text.size() - start;
- if (i + 1 < ranges.size()) {
- len = ranges[i + 1].start - start;
- }
+ // Collect the spans and untranslatable sections into one set of spans, sorted by first_char.
+ // This will effectively subdivide the string into multiple sections that can be individually
+ // pseudolocalized, while keeping the span indices synchronized.
+ std::vector<UnifiedSpan> merged_spans = MergeSpans(*string);
- if (ranges[i].update_start) {
- *ranges[i].update_start = localized.str.size();
- }
+ // All Span indices are UTF-16 based, according to the resources.arsc format expected by the
+ // runtime. So we will do all our processing in UTF-16, then convert back.
+ const std::u16string text16 = util::Utf8ToUtf16(*string->value->str);
- if (ranges[i].update_end) {
- *ranges[i].update_end = localized.str.size();
- }
+ // Convenient wrapper around the text that allows us to work with StringPieces.
+ const StringPiece16 text(text16);
+
+ // The new string.
+ std::string new_string = localizer.Start();
+
+ // The stack that keeps track of what nested Span we're in.
+ std::vector<size_t> span_stack;
+
+ // The current position in the original text.
+ uint32_t cursor = 0u;
+
+ // The current position in the new text.
+ uint32_t new_cursor = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_string.data()),
+ new_string.size(), false);
- if (ranges[i].toggle_translatability) {
- translatable = !translatable;
+ // We assume no nesting of untranslatable sections, since XLIFF doesn't allow it.
+ bool translatable = true;
+ size_t span_idx = 0u;
+ while (span_idx < merged_spans.size() || !span_stack.empty()) {
+ UnifiedSpan* span = span_idx >= merged_spans.size() ? nullptr : &merged_spans[span_idx];
+ UnifiedSpan* parent_span = span_stack.empty() ? nullptr : &merged_spans[span_stack.back()];
+
+ if (span != nullptr) {
+ if (parent_span == nullptr || parent_span->last_char > span->first_char) {
+ // There is no parent, or this span is the child of the parent.
+ // Pseudolocalize all the text until this span.
+ const StringPiece16 substr = text.substr(cursor, span->first_char - cursor);
+ cursor += substr.size();
+
+ // Pseudolocalize the substring.
+ std::string new_substr = util::Utf16ToUtf8(substr);
+ if (translatable) {
+ new_substr = localizer.Text(new_substr);
+ }
+ new_cursor += utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_substr.data()),
+ new_substr.size(), false);
+ new_string += new_substr;
+
+ // Rewrite the first_char.
+ span->first_char = new_cursor;
+ if (!span->tag) {
+ // An untranslatable section has begun!
+ translatable = false;
+ }
+ span_stack.push_back(span_idx);
+ ++span_idx;
+ continue;
+ }
}
- if (translatable) {
- localized.str += localizer.Text(original_text.substr(start, len));
- } else {
- localized.str += original_text.substr(start, len);
+ if (parent_span != nullptr) {
+ // There is a parent, and either this span is not a child of it, or there are no more spans.
+ // Pop this off the stack.
+ const StringPiece16 substr = text.substr(cursor, parent_span->last_char - cursor + 1);
+ cursor += substr.size();
+
+ // Pseudolocalize the substring.
+ std::string new_substr = util::Utf16ToUtf8(substr);
+ if (translatable) {
+ new_substr = localizer.Text(new_substr);
+ }
+ new_cursor += utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_substr.data()),
+ new_substr.size(), false);
+ new_string += new_substr;
+
+ parent_span->last_char = new_cursor - 1;
+ if (parent_span->tag) {
+ // An end to an untranslatable section.
+ translatable = true;
+ }
+ span_stack.pop_back();
}
}
- localized.str += localizer.End();
+ // Finish the pseudolocalization at the end of the string.
+ new_string += localizer.Text(util::Utf16ToUtf8(text.substr(cursor, text.size() - cursor)));
+ new_string += localizer.End();
+
+ StyleString localized;
+ localized.str = std::move(new_string);
+ // Convert the UnifiedSpans into regular Spans, skipping the UntranslatableSections.
+ for (UnifiedSpan& span : merged_spans) {
+ if (span.tag) {
+ localized.spans.push_back(Span{std::move(span.tag.value()), span.first_char, span.last_char});
+ }
+ }
return util::make_unique<StyledString>(pool->MakeRef(localized));
}
@@ -175,8 +233,7 @@ class Visitor : public RawValueVisitor {
if (sub_visitor.value) {
localized->values[i] = std::move(sub_visitor.item);
} else {
- localized->values[i] =
- std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
+ localized->values[i] = std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
}
}
}
@@ -210,8 +267,7 @@ class Visitor : public RawValueVisitor {
}
result += localizer_.End();
- std::unique_ptr<String> localized =
- util::make_unique<String>(pool_->MakeRef(result));
+ std::unique_ptr<String> localized = util::make_unique<String>(pool_->MakeRef(result));
localized->SetSource(string->GetSource());
localized->SetWeak(true);
item = std::move(localized);
@@ -282,14 +338,10 @@ void PseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
}
}
-/**
- * A value is pseudolocalizable if it does not define a locale (or is the
- * default locale)
- * and is translatable.
- */
+// A value is pseudolocalizable if it does not define a locale (or is the default locale) and is
+// translatable.
static bool IsPseudolocalizable(ResourceConfigValue* config_value) {
- const int diff =
- config_value->config.diff(ConfigDescription::DefaultConfig());
+ const int diff = config_value->config.diff(ConfigDescription::DefaultConfig());
if (diff & ConfigDescription::CONFIG_LOCALE) {
return false;
}
@@ -298,19 +350,16 @@ static bool IsPseudolocalizable(ResourceConfigValue* config_value) {
} // namespace
-bool PseudolocaleGenerator::Consume(IAaptContext* context,
- ResourceTable* table) {
+bool PseudolocaleGenerator::Consume(IAaptContext* context, ResourceTable* table) {
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- std::vector<ResourceConfigValue*> values =
- entry->FindValuesIf(IsPseudolocalizable);
-
+ std::vector<ResourceConfigValue*> values = entry->FindValuesIf(IsPseudolocalizable);
for (ResourceConfigValue* value : values) {
- PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
- &table->string_pool, entry.get());
- PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
- &table->string_pool, entry.get());
+ PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, &table->string_pool,
+ entry.get());
+ PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value, &table->string_pool,
+ entry.get());
}
}
}
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index 4db37db55eb7..b08e1dab35a9 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -25,7 +25,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
StringPool pool;
StyleString original_style;
original_style.str = "Hello world!";
- original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
+ original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}};
std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
@@ -34,22 +34,19 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
EXPECT_EQ(original_style.str, *new_string->value->str);
ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
- EXPECT_EQ(std::string("He").size(), new_string->value->spans[0].first_char);
- EXPECT_EQ(std::string("Hel").size(), new_string->value->spans[0].last_char);
- EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+ EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name);
+ EXPECT_EQ(std::u16string(u"H").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::u16string(u"Hello worl").size(), new_string->value->spans[0].last_char);
- EXPECT_EQ(std::string("Hello ").size(),
- new_string->value->spans[1].first_char);
- EXPECT_EQ(std::string("Hello w").size(),
- new_string->value->spans[1].last_char);
EXPECT_EQ(std::string("b"), *new_string->value->spans[1].name);
+ EXPECT_EQ(std::u16string(u"He").size(), new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::u16string(u"Hel").size(), new_string->value->spans[1].last_char);
- EXPECT_EQ(std::string("H").size(), new_string->value->spans[2].first_char);
- EXPECT_EQ(std::string("Hello worl").size(),
- new_string->value->spans[2].last_char);
- EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[2].name);
+ EXPECT_EQ(std::u16string(u"Hello ").size(), new_string->value->spans[2].first_char);
+ EXPECT_EQ(std::u16string(u"Hello w").size(), new_string->value->spans[2].last_char);
- original_style.spans.push_back(Span{"em", 0, 11u});
+ original_style.spans.insert(original_style.spans.begin(), Span{"em", 0, 11u});
new_string = PseudolocalizeStyledString(
util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
@@ -58,23 +55,128 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str);
ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
- EXPECT_EQ(std::string("[Ĥé").size(), new_string->value->spans[0].first_char);
- EXPECT_EQ(std::string("[Ĥéļ").size(), new_string->value->spans[0].last_char);
+ EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļð").size(), new_string->value->spans[0].last_char);
+
+ EXPECT_EQ(std::u16string(u"[Ĥ").size(), new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļ").size(), new_string->value->spans[1].last_char);
+
+ EXPECT_EQ(std::u16string(u"[Ĥé").size(), new_string->value->spans[2].first_char);
+ EXPECT_EQ(std::u16string(u"[Ĥéļ").size(), new_string->value->spans[2].last_char);
+
+ EXPECT_EQ(std::u16string(u"[Ĥéļļö ").size(), new_string->value->spans[3].first_char);
+ EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵ").size(), new_string->value->spans[3].last_char);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentNestedTags) {
+ StringPool pool;
+ StyleString original_style;
+ original_style.str = "bold";
+ original_style.spans = {Span{"b", 0, 3}, Span{"i", 0, 3}};
+
+ std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+ Pseudolocalizer::Method::kAccent, &pool);
+ ASSERT_NE(nullptr, new_string);
+ ASSERT_EQ(2u, new_string->value->spans.size());
+ EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+ EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[0].last_char);
+
+ EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
+ EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentTagsUnsorted) {
+ StringPool pool;
+ StyleString original_style;
+ original_style.str = "bold";
+ original_style.spans = {Span{"i", 2, 3}, Span{"b", 0, 1}};
+
+ std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+ Pseudolocalizer::Method::kAccent, &pool);
+ ASSERT_NE(nullptr, new_string);
+ ASSERT_EQ(2u, new_string->value->spans.size());
+ EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+ EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::u16string(u"[ɓ").size(), new_string->value->spans[0].last_char);
+
+ EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
+ EXPECT_EQ(std::u16string(u"[ɓö").size(), new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeNestedAndAdjacentTags) {
+ StringPool pool;
+ StyleString original_style;
+ original_style.str = "This sentence is not what you think it is at all.";
+ original_style.spans = {Span{"b", 16u, 19u}, Span{"em", 29u, 47u}, Span{"i", 38u, 40u},
+ Span{"b", 44u, 47u}};
+
+ std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+ Pseudolocalizer::Method::kAccent, &pool);
+ ASSERT_NE(nullptr, new_string);
+ ASSERT_EQ(4u, new_string->value->spans.size());
+ EXPECT_EQ(std::string(
+ "[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"),
+ *new_string->value->str);
+
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñö").size(), new_string->value->spans[0].last_char);
- EXPECT_EQ(std::string("[Ĥéļļö ").size(),
+ EXPECT_EQ(std::string("em"), *new_string->value->spans[1].name);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû").size(),
new_string->value->spans[1].first_char);
- EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(),
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(),
new_string->value->spans[1].last_char);
- EXPECT_EQ(std::string("[Ĥ").size(), new_string->value->spans[2].first_char);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(),
+ EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ").size(),
+ new_string->value->spans[2].first_char);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ î").size(),
new_string->value->spans[2].last_char);
- EXPECT_EQ(std::string("[").size(), new_string->value->spans[3].first_char);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(),
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[3].name);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ").size(),
+ new_string->value->spans[3].first_char);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(),
new_string->value->spans[3].last_char);
}
+TEST(PseudolocaleGeneratorTest, PseudolocalizePartsOfString) {
+ StringPool pool;
+ StyleString original_style;
+ original_style.str = "This should NOT be pseudolocalized.";
+ original_style.spans = {Span{"em", 4u, 14u}, Span{"i", 18u, 33u}};
+ std::unique_ptr<StyledString> original_string =
+ util::make_unique<StyledString>(pool.MakeRef(original_style));
+ original_string->untranslatable_sections = {UntranslatableSection{11u, 15u}};
+
+ std::unique_ptr<StyledString> new_string =
+ PseudolocalizeStyledString(original_string.get(), Pseudolocalizer::Method::kAccent, &pool);
+ ASSERT_NE(nullptr, new_string);
+ ASSERT_EQ(2u, new_string->value->spans.size());
+ EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"),
+ *new_string->value->str);
+
+ EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NO").size(), new_string->value->spans[0].last_char);
+
+ EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé").size(), new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžé").size(),
+ new_string->value->spans[1].last_char);
+}
+
TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -138,7 +240,7 @@ TEST(PseudolocaleGeneratorTest, RespectUntranslateableSections) {
{
StyleString original_style;
original_style.str = "Hello world!";
- original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
+ original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}};
auto styled_string =
util::make_unique<StyledString>(table->string_pool.MakeRef(original_style));