summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java26
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java10
-rw-r--r--core/java/android/bluetooth/le/ScanCallback.java6
-rw-r--r--core/java/android/content/Intent.java10
-rwxr-xr-xcore/java/android/provider/Settings.java18
-rw-r--r--core/java/android/service/autofill/CustomDescription.java14
-rw-r--r--core/java/android/text/StaticLayout.java67
-rw-r--r--core/java/android/util/FeatureFlagUtils.java58
-rw-r--r--core/java/android/view/autofill/AutofillManager.java183
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl1
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl9
-rw-r--r--core/java/android/webkit/UserPackage.java2
-rw-r--r--core/java/com/android/server/BootReceiver.java49
-rw-r--r--core/jni/android_text_StaticLayout.cpp24
-rw-r--r--core/proto/android/server/windowmanagerservice.proto8
-rw-r--r--core/tests/featureflagtests/Android.mk19
-rw-r--r--core/tests/featureflagtests/AndroidManifest.xml33
-rw-r--r--core/tests/featureflagtests/AndroidTest.xml28
-rw-r--r--core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java72
-rw-r--r--media/java/android/media/AudioManager.java29
-rw-r--r--media/java/android/media/MediaDescription.java27
-rw-r--r--media/java/android/media/MediaFile.java2
-rw-r--r--media/java/android/media/session/MediaSession.java23
-rw-r--r--packages/BackupRestoreConfirmation/res/values-mr/strings.xml4
-rw-r--r--packages/SystemUI/res/layout/global_actions_item.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java23
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java15
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java49
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java91
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java71
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/PendingUi.java82
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java113
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java56
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java7
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java78
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java131
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java233
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java44
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java17
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java93
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java12
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowLayersController.java3
-rw-r--r--services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java30
-rw-r--r--tools/aapt2/NameMangler.h7
-rw-r--r--tools/aapt2/ResourceParser.cpp29
-rw-r--r--tools/aapt2/cmd/Link.cpp179
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.cpp6
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Android.mk2
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/Android.mk29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml30
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java34
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk28
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml21
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml28
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml21
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml27
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java53
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/Android.mk2
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/Android.mk (renamed from tools/aapt2/integration-tests/AppOne/Android.mk)6
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml (renamed from tools/aapt2/integration-tests/AppOne/AndroidManifest.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets/subdir/subsubdir/test.txt (renamed from tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets/test.txt (renamed from tools/aapt2/integration-tests/AppOne/assets/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets2/new.txt (renamed from tools/aapt2/integration-tests/AppOne/assets2/new.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets2/test.txt (renamed from tools/aapt2/integration-tests/AppOne/assets2/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/cheap_transparency.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png)bin397 -> 397 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/complex.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png)bin409 -> 409 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/icon.png)bin2341 -> 2341 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/image.xml (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/image.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/outline_8x8.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png)bin191 -> 191 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_off_center_outline_32x16.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png)bin196 -> 196 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_outline_32x16.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png)bin181 -> 181 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/test.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png)bin124 -> 124 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png)bin105 -> 105 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_optical_bounds_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png)bin103 -> 103 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png)bin103 -> 103 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_optical_bounds_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png)bin105 -> 105 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-italic.ttf (renamed from tools/aapt2/integration-tests/AppOne/res/font/myfont-italic.ttf)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-normal.ttf (renamed from tools/aapt2/integration-tests/AppOne/res/font/myfont-normal.ttf)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont.xml (renamed from tools/aapt2/integration-tests/AppOne/res/font/myfont.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/layout-v21/main.xml (renamed from tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml)1
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml (renamed from tools/aapt2/integration-tests/AppOne/res/layout/main.xml)1
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/layout/special.xml (renamed from tools/aapt2/integration-tests/AppOne/res/layout/special.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/navigation/home.xml (renamed from tools/aapt2/integration-tests/AppOne/res/navigation/home.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/raw/test.txt (renamed from tools/aapt2/integration-tests/AppOne/res/raw/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/transition/transition_set.xml (renamed from tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values-v4/styles.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values/colors.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values/colors.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values/styles.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values/styles.xml)2
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values/test.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values/test.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/src/com/android/aapt/app/one/AppOne.java (renamed from tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk (renamed from tools/aapt2/integration-tests/StaticLibOne/Android.mk)4
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/AndroidManifest.xml (renamed from tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/res/layout/layout.xml (renamed from tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/res/values/values.xml (renamed from tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java (renamed from tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk (renamed from tools/aapt2/integration-tests/StaticLibTwo/Android.mk)4
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/AndroidManifest.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/drawable/vector.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/layout/layout_two.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/values/values.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java (renamed from tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java)0
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp5
-rw-r--r--tools/aapt2/link/Linkers.h88
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp214
-rw-r--r--tools/aapt2/link/ReferenceLinker.h87
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp31
-rw-r--r--tools/aapt2/link/TableMerger.cpp96
-rw-r--r--tools/aapt2/link/TableMerger.h86
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp56
-rw-r--r--tools/aapt2/xml/XmlDom.cpp12
-rw-r--r--tools/aapt2/xml/XmlDom.h3
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp18
-rw-r--r--tools/aapt2/xml/XmlPullParser.cpp7
-rw-r--r--tools/aapt2/xml/XmlPullParser.h3
-rw-r--r--tools/aapt2/xml/XmlUtil.cpp10
-rw-r--r--tools/aapt2/xml/XmlUtil.h15
-rw-r--r--tools/locked_region_code_injection/Android.mk2
126 files changed, 2371 insertions, 905 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f79dbf9ee4f2..4e258a3a4b47 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1863,8 +1863,18 @@ public class Activity extends ContextThemeWrapper
getApplication().dispatchActivityStopped(this);
mTranslucentCallback = null;
mCalled = true;
- if (isFinishing() && mAutoFillResetNeeded) {
- getAutofillManager().commit();
+
+ if (isFinishing()) {
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().commit();
+ } else if (mIntent != null
+ && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
+ // Activity was launched when user tapped a link in the Autofill Save UI - since
+ // user launched another activity, the Save UI should not be restored when this
+ // activity is finished.
+ getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
+ mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
+ }
}
}
@@ -5491,6 +5501,13 @@ public class Activity extends ContextThemeWrapper
} else {
mParent.finishFromChild(this);
}
+
+ // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
+ // be restored now.
+ if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
+ getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,
+ mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
+ }
}
/**
@@ -6225,6 +6242,11 @@ public class Activity extends ContextThemeWrapper
}
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
+
+ final AutofillManager afm = getAutofillManager();
+ if (afm != null) {
+ afm.dump(prefix, writer);
+ }
}
/**
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 9a0f70fe9f72..c8ed7ef719d9 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -345,6 +345,7 @@ public final class BluetoothLeScanner {
private List<List<ResultStorageDescriptor>> mResultStorages;
// mLeHandle 0: not registered
+ // -2: registration failed because app is scanning to frequently
// -1: scan stopped or registration failed
// > 0: registered and scan started
private int mScannerId;
@@ -365,7 +366,7 @@ public final class BluetoothLeScanner {
public void startRegistration() {
synchronized (this) {
// Scan stopped.
- if (mScannerId == -1) return;
+ if (mScannerId == -1 || mScannerId == -2) return;
try {
mBluetoothGatt.registerScanner(this, mWorkSource);
wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
@@ -379,6 +380,10 @@ public final class BluetoothLeScanner {
// Registration timed out or got exception, reset scannerId to -1 so no
// subsequent operations can proceed.
if (mScannerId == 0) mScannerId = -1;
+
+ // If scanning too frequently, don't report anything to the app.
+ if (mScannerId == -2) return;
+
postCallbackError(mScanCallback,
ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
}
@@ -438,6 +443,9 @@ public final class BluetoothLeScanner {
Log.e(TAG, "fail to start le scan: " + e);
mScannerId = -1;
}
+ } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) {
+ // applicaiton was scanning too frequently
+ mScannerId = -2;
} else {
// registration failed
mScannerId = -1;
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index fcbc2c74f0dd..53d9310a1236 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -51,6 +51,12 @@ public abstract class ScanCallback {
*/
public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
+ /**
+ * Fails to start scan as application tries to scan too frequently.
+ * @hide
+ */
+ public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6;
+
static final int NO_ERROR = 0;
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a9dbdd5268a6..08acfb651b18 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3955,6 +3955,16 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_SETUP_WIZARD = "android.intent.category.SETUP_WIZARD";
/**
+ * This is the home activity, that is the activity that serves as the launcher app
+ * from there the user can start other apps. Often components with lower/higher
+ * priority intent filters handle the home intent, for example SetupWizard, to
+ * setup the device and we need to be able to distinguish the home app from these
+ * setup helpers.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_LAUNCHER_APP = "android.intent.category.LAUNCHER_APP";
+ /**
* This activity is a preference panel.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0ff4adc25ab7..1741755c2750 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1750,6 +1750,10 @@ public final class Settings {
return true;
}
+ public int getCurrentGeneration() {
+ return mCurrentGeneration;
+ }
+
private int readCurrentGeneration() {
try {
return mArray.get(mIndex);
@@ -1858,6 +1862,7 @@ public final class Settings {
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
final boolean isSelf = (userHandle == UserHandle.myUserId());
+ int currentGeneration = -1;
if (isSelf) {
synchronized (NameValueCache.this) {
if (mGenerationTracker != null) {
@@ -1871,6 +1876,9 @@ public final class Settings {
} else if (mValues.containsKey(name)) {
return mValues.get(name);
}
+ if (mGenerationTracker != null) {
+ currentGeneration = mGenerationTracker.getCurrentGeneration();
+ }
}
}
} else {
@@ -1961,7 +1969,10 @@ public final class Settings {
});
}
}
- mValues.put(name, value);
+ if (mGenerationTracker != null && currentGeneration ==
+ mGenerationTracker.getCurrentGeneration()) {
+ mValues.put(name, value);
+ }
}
} else {
if (LOCAL_LOGV) Log.i(TAG, "call-query of user " + userHandle
@@ -2002,7 +2013,10 @@ public final class Settings {
String value = c.moveToNext() ? c.getString(0) : null;
synchronized (NameValueCache.this) {
- mValues.put(name, value);
+ if(mGenerationTracker != null &&
+ currentGeneration == mGenerationTracker.getCurrentGeneration()) {
+ mValues.put(name, value);
+ }
}
if (LOCAL_LOGV) {
Log.v(TAG, "cache miss [" + mUri.getLastPathSegment() + "]: " +
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 4f06bd759e47..9a4cbc415d64 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -19,6 +19,8 @@ package android.service.autofill;
import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -130,6 +132,18 @@ public final class CustomDescription implements Parcelable {
/**
* Default constructor.
*
+ * <p><b>Note:</b> If any child view of presentation triggers a
+ * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent) pending intent
+ * on click}, such {@link PendingIntent} must follow the restrictions below, otherwise
+ * it might not be triggered or the Save affordance might not be shown when its activity
+ * is finished:
+ * <ul>
+ * <li>It cannot be created with the {@link PendingIntent#FLAG_IMMUTABLE} flag.
+ * <li>It must be a PendingIntent for an {@link Activity}.
+ * <li>The activity must call {@link Activity#finish()} when done.
+ * <li>The activity should not launch other activities.
+ * </ul>
+ *
* @param parentPresentation template presentation with (optional) children views.
*/
public Builder(RemoteViews parentPresentation) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 1725d40eec71..888bb00b6b58 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -352,15 +352,6 @@ public class StaticLayout extends Layout {
public Builder setIndents(@Nullable int[] leftIndents, @Nullable int[] rightIndents) {
mLeftIndents = leftIndents;
mRightIndents = rightIndents;
- int leftLen = leftIndents == null ? 0 : leftIndents.length;
- int rightLen = rightIndents == null ? 0 : rightIndents.length;
- int[] indents = new int[Math.max(leftLen, rightLen)];
- for (int i = 0; i < indents.length; i++) {
- int leftMargin = i < leftLen ? leftIndents[i] : 0;
- int rightMargin = i < rightLen ? rightIndents[i] : 0;
- indents[i] = leftMargin + rightMargin;
- }
- nSetIndents(mNativePtr, indents);
return this;
}
@@ -485,8 +476,8 @@ public class StaticLayout extends Layout {
private int mMaxLines;
private int mBreakStrategy;
private int mHyphenationFrequency;
- private int[] mLeftIndents;
- private int[] mRightIndents;
+ @Nullable private int[] mLeftIndents;
+ @Nullable private int[] mRightIndents;
private int mJustificationMode;
private boolean mAddLastLineLineSpacing;
@@ -689,6 +680,22 @@ public class StaticLayout extends Layout {
if (source instanceof Spanned)
spanned = (Spanned) source;
+ final int[] indents;
+ if (mLeftIndents != null || mRightIndents != null) {
+ final int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
+ final int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
+ final int indentsLen = Math.max(leftLen, rightLen);
+ indents = new int[indentsLen];
+ for (int i = 0; i < leftLen; i++) {
+ indents[i] = mLeftIndents[i];
+ }
+ for (int i = 0; i < rightLen; i++) {
+ indents[i] += mRightIndents[i];
+ }
+ } else {
+ indents = null;
+ }
+
int paraEnd;
for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
@@ -773,24 +780,9 @@ public class StaticLayout extends Layout {
firstWidth, firstWidthLineCount, restWidth,
variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
// TODO: Support more justification mode, e.g. letter spacing, stretching.
- b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE);
- if (mLeftIndents != null || mRightIndents != null) {
- // TODO(performance): it would be better to do this once per layout rather
- // than once per paragraph, but that would require a change to the native
- // interface.
- int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
- int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
- int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
- int[] indents = new int[indentsLen];
- for (int i = 0; i < indentsLen; i++) {
- int leftMargin = mLeftIndents == null ? 0 :
- mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
- int rightMargin = mRightIndents == null ? 0 :
- mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
- indents[i] = leftMargin + rightMargin;
- }
- nSetIndents(b.mNativePtr, indents);
- }
+ b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
+ (indents != null && indents.length > mLineCount) ? indents : null,
+ mLineCount);
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
@@ -1507,13 +1499,14 @@ public class StaticLayout extends Layout {
private static native void nSetLocales(long nativePtr, String locales,
long[] nativeHyphenators);
- private static native void nSetIndents(long nativePtr, int[] indents);
-
// Set up paragraph text and settings; done as one big method to minimize jni crossings
- private static native void nSetupParagraph(long nativePtr, char[] text, int length,
- float firstWidth, int firstWidthLineCount, float restWidth,
- int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency,
- boolean isJustified);
+ private static native void nSetupParagraph(
+ @NonNull long nativePtr, @NonNull char[] text, @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth, @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops,
+ int defaultTabStop, @BreakStrategy int breakStrategy,
+ @HyphenationFrequency int hyphenationFrequency, boolean isJustified,
+ @Nullable int[] indents, @IntRange(from = 0) int intentsOffset);
private static native float nAddStyleRun(long nativePtr, long nativePaint, int start, int end,
boolean isRtl);
@@ -1597,6 +1590,6 @@ public class StaticLayout extends Layout {
// breaks, widths, and flags should all have the same length
}
- private int[] mLeftIndents;
- private int[] mRightIndents;
+ @Nullable private int[] mLeftIndents;
+ @Nullable private int[] mRightIndents;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
new file mode 100644
index 000000000000..5838f9590c9d
--- /dev/null
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+import android.os.SystemProperties;
+import android.text.TextUtils;
+
+import java.util.Map;
+
+/**
+ * Util class to get feature flag information.
+ *
+ * @hide
+ */
+public class FeatureFlagUtils {
+
+ public static final String FFLAG_PREFIX = "sys.fflag.";
+ public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
+
+ /**
+ * Whether or not a flag is enabled.
+ *
+ * @param feature the flag name
+ * @return true if the flag is enabled (either by default in system, or override by user)
+ */
+ public static boolean isEnabled(String feature) {
+ // Tries to get feature flag from system property.
+ // Step 1: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
+ String value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
+ if (!TextUtils.isEmpty(value)) {
+ return Boolean.parseBoolean(value);
+ }
+ // Step 2: check if feature flag has any default value. Flag name: sys.fflag.<feature>
+ value = SystemProperties.get(FFLAG_PREFIX + feature);
+ return Boolean.parseBoolean(value);
+ }
+
+ /**
+ * Returns all feature flags in their raw form.
+ */
+ public static Map<String, String> getAllFeatureFlags() {
+ return null;
+ }
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 29e5523ceb7c..61cbce976844 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -30,12 +30,14 @@ import android.content.IntentSender;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DebugUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
@@ -44,6 +46,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -154,8 +157,15 @@ public final class AutofillManager {
public static final String EXTRA_CLIENT_STATE =
"android.view.autofill.extra.CLIENT_STATE";
- static final String SESSION_ID_TAG = "android:sessionId";
- static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
+
+ /** @hide */
+ public static final String EXTRA_RESTORE_SESSION_TOKEN =
+ "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
+
+ private static final String SESSION_ID_TAG = "android:sessionId";
+ private static final String STATE_TAG = "android:state";
+ private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
+
/** @hide */ public static final int ACTION_START_SESSION = 1;
/** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
@@ -175,6 +185,44 @@ public final class AutofillManager {
public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
/**
+ * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
+ *
+ * @hide
+ */
+ public static final int PENDING_UI_OPERATION_CANCEL = 1;
+
+ /**
+ * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
+ *
+ * @hide
+ */
+ public static final int PENDING_UI_OPERATION_RESTORE = 2;
+
+ /**
+ * Initial state of the autofill context, set when there is no session (i.e., when
+ * {@link #mSessionId} is {@link #NO_SESSION}).
+ *
+ * @hide
+ */
+ public static final int STATE_UNKNOWN = 1;
+
+ /**
+ * State where the autofill context hasn't been {@link #commit() finished} nor
+ * {@link #cancel() canceled} yet.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 2;
+
+ /**
+ * State where the autofill context has been {@link #commit() finished} but the server still has
+ * a session because the Save UI hasn't been dismissed yet.
+ *
+ * @hide
+ */
+ public static final int STATE_SHOWING_SAVE_UI = 4;
+
+ /**
* Makes an authentication id from a request id and a dataset id.
*
* @param requestId The request id.
@@ -233,6 +281,9 @@ public final class AutofillManager {
private int mSessionId = NO_SESSION;
@GuardedBy("mLock")
+ private int mState = STATE_UNKNOWN;
+
+ @GuardedBy("mLock")
private boolean mEnabled;
/** If a view changes to this mapping the autofill operation was successful */
@@ -344,12 +395,13 @@ public final class AutofillManager {
synchronized (mLock) {
mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
- if (mSessionId != NO_SESSION) {
+ if (isActiveLocked()) {
Log.w(TAG, "New session was started before onCreate()");
return;
}
mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
+ mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
if (mSessionId != NO_SESSION) {
ensureServiceClientAddedIfNeededLocked();
@@ -363,6 +415,7 @@ public final class AutofillManager {
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
mSessionId = NO_SESSION;
+ mState = STATE_UNKNOWN;
} else {
if (sDebug) {
Log.d(TAG, "session " + mSessionId + " was restored");
@@ -387,7 +440,7 @@ public final class AutofillManager {
*/
public void onVisibleForAutofill() {
synchronized (mLock) {
- if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
+ if (mEnabled && isActiveLocked() && mTrackedViews != null) {
mTrackedViews.onVisibleForAutofillLocked();
}
}
@@ -408,7 +461,9 @@ public final class AutofillManager {
if (mSessionId != NO_SESSION) {
outState.putInt(SESSION_ID_TAG, mSessionId);
}
-
+ if (mState != STATE_UNKNOWN) {
+ outState.putInt(STATE_TAG, mState);
+ }
if (mLastAutofilledData != null) {
outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
}
@@ -514,7 +569,7 @@ public final class AutofillManager {
final AutofillId id = getAutofillId(view);
final AutofillValue value = view.getAutofillValue();
- if (mSessionId == NO_SESSION) {
+ if (!isActiveLocked()) {
// Starts new session.
startSessionLocked(id, null, value, flags);
} else {
@@ -541,7 +596,7 @@ public final class AutofillManager {
synchronized (mLock) {
ensureServiceClientAddedIfNeededLocked();
- if (mEnabled && mSessionId != NO_SESSION) {
+ if (mEnabled && isActiveLocked()) {
final AutofillId id = getAutofillId(view);
// Update focus on existing session.
@@ -582,7 +637,7 @@ public final class AutofillManager {
private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
boolean isVisible, boolean virtual) {
synchronized (mLock) {
- if (mEnabled && mSessionId != NO_SESSION) {
+ if (mEnabled && isActiveLocked()) {
final AutofillId id = virtual ? getAutofillId(view, virtualId)
: view.getAutofillId();
if (!isVisible && mFillableIds != null) {
@@ -636,7 +691,7 @@ public final class AutofillManager {
} else {
final AutofillId id = getAutofillId(view, virtualId);
- if (mSessionId == NO_SESSION) {
+ if (!isActiveLocked()) {
// Starts new session.
startSessionLocked(id, bounds, null, flags);
} else {
@@ -665,7 +720,7 @@ public final class AutofillManager {
synchronized (mLock) {
ensureServiceClientAddedIfNeededLocked();
- if (mEnabled && mSessionId != NO_SESSION) {
+ if (mEnabled && isActiveLocked()) {
final AutofillId id = getAutofillId(view, virtualId);
// Update focus on existing session.
@@ -709,7 +764,7 @@ public final class AutofillManager {
}
}
- if (!mEnabled || mSessionId == NO_SESSION) {
+ if (!mEnabled || !isActiveLocked()) {
return;
}
@@ -737,7 +792,7 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- if (!mEnabled || mSessionId == NO_SESSION) {
+ if (!mEnabled || !isActiveLocked()) {
return;
}
@@ -762,7 +817,7 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- if (!mEnabled && mSessionId == NO_SESSION) {
+ if (!mEnabled && !isActiveLocked()) {
return;
}
@@ -786,7 +841,7 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- if (!mEnabled && mSessionId == NO_SESSION) {
+ if (!mEnabled && !isActiveLocked()) {
return;
}
@@ -868,7 +923,7 @@ public final class AutofillManager {
if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
synchronized (mLock) {
- if (mSessionId == NO_SESSION || data == null) {
+ if (!isActiveLocked() || data == null) {
return;
}
final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
@@ -895,13 +950,19 @@ public final class AutofillManager {
@NonNull AutofillValue value, int flags) {
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
- + ", flags=" + flags);
+ + ", flags=" + flags + ", state=" + mState);
+ }
+ if (mState != STATE_UNKNOWN) {
+ if (sDebug) Log.d(TAG, "not starting session for " + id + " on state " + mState);
+ return;
}
-
try {
mSessionId = mService.startSession(mContext.getActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, mContext.getOpPackageName());
+ if (mSessionId != NO_SESSION) {
+ mState = STATE_ACTIVE;
+ }
final AutofillClient client = getClientLocked();
if (client != null) {
client.autofillCallbackResetableStateAvailable();
@@ -912,7 +973,9 @@ public final class AutofillManager {
}
private void finishSessionLocked() {
- if (sVerbose) Log.v(TAG, "finishSessionLocked()");
+ if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + mState);
+
+ if (!isActiveLocked()) return;
try {
mService.finishSession(mSessionId, mContext.getUserId());
@@ -920,12 +983,13 @@ public final class AutofillManager {
throw e.rethrowFromSystemServer();
}
- mTrackedViews = null;
- mSessionId = NO_SESSION;
+ resetSessionLocked();
}
private void cancelSessionLocked() {
- if (sVerbose) Log.v(TAG, "cancelSessionLocked()");
+ if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + mState);
+
+ if (!isActiveLocked()) return;
try {
mService.cancelSession(mSessionId, mContext.getUserId());
@@ -938,7 +1002,9 @@ public final class AutofillManager {
private void resetSessionLocked() {
mSessionId = NO_SESSION;
+ mState = STATE_UNKNOWN;
mTrackedViews = null;
+ mFillableIds = null;
}
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
@@ -947,7 +1013,6 @@ public final class AutofillManager {
Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
+ ", value=" + value + ", action=" + action + ", flags=" + flags);
}
-
boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
try {
@@ -958,6 +1023,7 @@ public final class AutofillManager {
if (newId != mSessionId) {
if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
mSessionId = newId;
+ mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
final AutofillClient client = getClientLocked();
if (client != null) {
client.autofillCallbackResetableStateAvailable();
@@ -1219,6 +1285,27 @@ public final class AutofillManager {
}
}
+ private void setSaveUiState(int sessionId, boolean shown) {
+ if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
+ synchronized (mLock) {
+ if (mSessionId != NO_SESSION) {
+ // Race condition: app triggered a new session after the previous session was
+ // finished but before server called setSaveUiState() - need to cancel the new
+ // session to avoid further inconsistent behavior.
+ Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
+ + ") called on existing session " + mSessionId + "; cancelling it");
+ cancelSessionLocked();
+ }
+ if (shown) {
+ mSessionId = sessionId;
+ mState = STATE_SHOWING_SAVE_UI;
+ } else {
+ mSessionId = NO_SESSION;
+ mState = STATE_UNKNOWN;
+ }
+ }
+ }
+
private void requestHideFillUi(AutofillId id) {
final View anchor = findView(id);
if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
@@ -1329,6 +1416,46 @@ public final class AutofillManager {
return mService != null;
}
+ /** @hide */
+ public void onPendingSaveUi(int operation, IBinder token) {
+ if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
+
+ synchronized (mLock) {
+ try {
+ mService.onPendingSaveUi(operation, token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /** @hide */
+ public void dump(String outerPrefix, PrintWriter pw) {
+ pw.print(outerPrefix); pw.println("AutofillManager:");
+ final String pfx = outerPrefix + " ";
+ pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
+ pw.print(pfx); pw.print("state: "); pw.println(
+ DebugUtils.flagsToString(AutofillManager.class, "STATE_", mState));
+ pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
+ pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
+ pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
+ pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
+ pw.print(pfx); pw.print("tracked views: ");
+ if (mTrackedViews == null) {
+ pw.println("null");
+ } else {
+ final String pfx2 = pfx + " ";
+ pw.println();
+ pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
+ pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
+ }
+ pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
+ }
+
+ private boolean isActiveLocked() {
+ return mState == STATE_ACTIVE;
+ }
+
private void post(Runnable runnable) {
final AutofillClient client = getClientLocked();
if (client == null) {
@@ -1668,12 +1795,12 @@ public final class AutofillManager {
}
@Override
- public void startIntentSender(IntentSender intentSender) {
+ public void startIntentSender(IntentSender intentSender, Intent intent) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.post(() -> {
try {
- afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
}
@@ -1691,5 +1818,13 @@ public final class AutofillManager {
);
}
}
+
+ @Override
+ public void setSaveUiState(int sessionId, boolean shown) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() ->afm.setSaveUiState(sessionId, shown));
+ }
+ }
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 627afa7f8364..6bd9bec368c8 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -49,4 +49,5 @@ interface IAutoFillManager {
void disableOwnedAutofillServices(int userId);
boolean isServiceSupported(int userId);
boolean isServiceEnabled(int userId, String packageName);
+ void onPendingSaveUi(int operation, IBinder token);
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index d18b1816e09e..0eae85860383 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -72,7 +72,12 @@ oneway interface IAutoFillManagerClient {
void notifyNoFillUi(int sessionId, in AutofillId id);
/**
- * Starts the provided intent sender
+ * Starts the provided intent sender.
*/
- void startIntentSender(in IntentSender intentSender);
+ void startIntentSender(in IntentSender intentSender, in Intent intent);
+
+ /**
+ * Sets the state of the Autofill Save UI for a given session.
+ */
+ void setSaveUiState(int sessionId, boolean shown);
}
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 892008948e87..9da64559d0fe 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -83,7 +83,7 @@ public class UserPackage {
* supported by the current framework version.
*/
public static boolean hasCorrectTargetSdkVersion(PackageInfo packageInfo) {
- return packageInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O;
+ return packageInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O_MR1;
}
public UserInfo getUserInfo() {
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 18990cff1f83..43544862b5ec 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -31,13 +31,13 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.provider.Downloads;
+import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -106,6 +106,11 @@ public class BootReceiver extends BroadcastReceiver {
"powerctl_shutdown_time_ms:([0-9]+):([0-9]+)";
private static final int UMOUNT_STATUS_NOT_AVAILABLE = 4; // should match with init/reboot.h
+ // Location of file with metrics recorded during shutdown
+ private static final String SHUTDOWN_METRICS_FILE = "/data/system/shutdown-metrics.txt";
+
+ private static final String SHUTDOWN_TRON_METRICS_PREFIX = "shutdown_";
+
@Override
public void onReceive(final Context context, Intent intent) {
// Log boot events in the background to avoid blocking the main thread with I/O
@@ -232,6 +237,7 @@ public class BootReceiver extends BroadcastReceiver {
logFsShutdownTime();
logFsMountTime();
addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
+ logSystemServerShutdownTimeMetrics();
// Scan existing tombstones (in case any new ones appeared)
File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
@@ -380,6 +386,47 @@ public class BootReceiver extends BroadcastReceiver {
}
}
+ // TODO b/64815357 Move to bootstat.cpp and log AbsoluteRebootTime
+ private static void logSystemServerShutdownTimeMetrics() {
+ File metricsFile = new File(SHUTDOWN_METRICS_FILE);
+ String metricsStr = null;
+ if (metricsFile.exists()) {
+ try {
+ metricsStr = FileUtils.readTextFile(metricsFile, 0, null);
+ } catch (IOException e) {
+ Slog.e(TAG, "Problem reading " + metricsFile, e);
+ }
+ }
+ if (!TextUtils.isEmpty(metricsStr)) {
+ String[] array = metricsStr.split(",");
+ for (String keyValueStr : array) {
+ String[] keyValue = keyValueStr.split(":");
+ if (keyValue.length != 2) {
+ Slog.e(TAG, "Wrong format of shutdown metrics - " + metricsStr);
+ continue;
+ }
+ // Ignore keys that are not indended for tron
+ if (keyValue[0].startsWith(SHUTDOWN_TRON_METRICS_PREFIX)) {
+ logTronShutdownMetric(keyValue[0], keyValue[1]);
+ }
+ }
+ }
+ metricsFile.delete();
+ }
+
+ private static void logTronShutdownMetric(String metricName, String valueStr) {
+ int value;
+ try {
+ value = Integer.parseInt(valueStr);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Cannot parse metric " + metricName + " int value - " + valueStr);
+ return;
+ }
+ if (value >= 0) {
+ MetricsLogger.histogram(null, metricName, value);
+ }
+ }
+
private static void logFsShutdownTime() {
File f = null;
for (String fileName : LAST_KMSG_FILES) {
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index ed6942eb5423..15ab8f7d661b 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -57,7 +57,7 @@ static JLineBreaksID gLineBreaks_fieldID;
static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
- jboolean isJustified) {
+ jboolean isJustified, jintArray indents, jint insetsOffset) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
b->resize(length);
env->GetCharArrayRegion(text, 0, length, b->buffer());
@@ -72,6 +72,18 @@ static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray tex
b->setStrategy(static_cast<minikin::BreakStrategy>(strategy));
b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
b->setJustified(isJustified);
+
+ // TODO: copy indents only once when LineBreaker is started to be used.
+ if (indents != nullptr) {
+ // If indents is not null, it is guaranteed that lineOffset is less than the size of array.
+ ScopedIntArrayRO indentArr(env, indents);
+ std::vector<float> indentVec(
+ indentArr.get() + insetsOffset, indentArr.get() + indentArr.size());
+ b->setIndents(indentVec);
+ } else {
+ b->setIndents(std::vector<float>());
+ }
+
}
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -164,13 +176,6 @@ static void nSetLocales(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocale
b->setLocales(localeNames.c_str(), hyphVec);
}
-static void nSetIndents(JNIEnv* env, jclass, jlong nativePtr, jintArray indents) {
- ScopedIntArrayRO indentArr(env, indents);
- std::vector<float> indentVec(indentArr.get(), indentArr.get() + indentArr.size());
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
- b->setIndents(indentVec);
-}
-
// Basically similar to Paint.getTextRunAdvances but with C++ interface
static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
jint end, jboolean isRtl) {
@@ -211,8 +216,7 @@ static const JNINativeMethod gMethods[] = {
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
{"nLoadHyphenator", "(Ljava/nio/ByteBuffer;III)J", (void*) nLoadHyphenator},
{"nSetLocales", "(JLjava/lang/String;[J)V", (void*) nSetLocales},
- {"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph},
- {"nSetIndents", "(J[I)V", (void*) nSetIndents},
+ {"nSetupParagraph", "(J[CIFIF[IIIIZ[II)V", (void*) nSetupParagraph},
{"nAddStyleRun", "(JJIIZ)F", (void*) nAddStyleRun},
{"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
{"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 7fb48028494d..ee8a6dc452b7 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -91,6 +91,8 @@ message DisplayProto {
repeated WindowTokenProto ime_windows = 7;
int32 dpi = 8;
.android.view.DisplayInfoProto display_info = 9;
+ int32 rotation = 10;
+ ScreenRotationAnimationProto screen_rotation_animation = 11;
}
@@ -170,4 +172,10 @@ message WindowStateAnimatorProto {
message WindowSurfaceControllerProto {
bool shown = 1;
int32 layer = 2;
+}
+
+/* represents ScreenRotationAnimation */
+message ScreenRotationAnimationProto {
+ bool started = 1;
+ bool animation_running = 2;
} \ No newline at end of file
diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk
new file mode 100644
index 000000000000..f2d205885c20
--- /dev/null
+++ b/core/tests/featureflagtests/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_DX_FLAGS := --core-library
+LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests
+
+LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/featureflagtests/AndroidManifest.xml b/core/tests/featureflagtests/AndroidManifest.xml
new file mode 100644
index 000000000000..b8ffacbe5aa8
--- /dev/null
+++ b/core/tests/featureflagtests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.frameworks.coretests.featureflagtests"
+ android:sharedUserId="android.uid.system">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.coretests.featureflagtests"
+ android:label="Frameworks FeatureFlagUtils Tests" />
+
+</manifest>
diff --git a/core/tests/featureflagtests/AndroidTest.xml b/core/tests/featureflagtests/AndroidTest.xml
new file mode 100644
index 000000000000..44f9c3e37853
--- /dev/null
+++ b/core/tests/featureflagtests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<configuration description="Runs Frameworks Utility Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksCoreFeatureFlagTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksCoreFeatureFlagTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.coretests.featureflagtests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
new file mode 100644
index 000000000000..8fee1d11a954
--- /dev/null
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.util;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.SystemProperties;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FeatureFlagUtilsTest {
+
+ private static final String TEST_FEATURE_NAME = "feature_foobar";
+
+ @Before
+ public void setUp() {
+ cleanup();
+ }
+
+ @After
+ public void tearDown() {
+ cleanup();
+ }
+
+ private void cleanup() {
+ SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "");
+ SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "");
+ }
+
+ @Test
+ public void testGetFlag_enabled_shouldReturnTrue() {
+ SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "true");
+
+ assertTrue(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ }
+
+ @Test
+ public void testGetFlag_override_shouldReturnTrue() {
+ SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
+ SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "true");
+
+ assertTrue(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ }
+
+ @Test
+ public void testGetFlag_notSet_shouldReturnFalse() {
+ assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ }
+
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 05be088fb2ac..eea4628e83af 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -16,12 +16,12 @@
package android.media;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.NotificationManager;
@@ -397,6 +397,19 @@ public class AudioManager {
*/
public static final int ADJUST_TOGGLE_MUTE = 101;
+ /** @hide */
+ public static final String adjustToString(int adj) {
+ switch (adj) {
+ case ADJUST_RAISE: return "ADJUST_RAISE";
+ case ADJUST_LOWER: return "ADJUST_LOWER";
+ case ADJUST_SAME: return "ADJUST_SAME";
+ case ADJUST_MUTE: return "ADJUST_MUTE";
+ case ADJUST_UNMUTE: return "ADJUST_UNMUTE";
+ case ADJUST_TOGGLE_MUTE: return "ADJUST_TOGGLE_MUTE";
+ default: return new StringBuilder("unknown adjust mode ").append(adj).toString();
+ }
+ }
+
// Flags should be powers of 2!
/**
@@ -2364,8 +2377,8 @@ public class AudioManager {
* usecases such as voice memo recording, or speech recognition.
* Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
* as the playback of a song or a video.
- * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK}
- * and {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}.
+ * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
+ * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS} and {@link #AUDIOFOCUS_FLAG_LOCK}.
* <br>Use 0 when not using any flags for the request, which behaves like
* {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
* focus is granted immediately, or the grant request fails because the system is in a
@@ -2377,6 +2390,7 @@ public class AudioManager {
* @throws IllegalArgumentException
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
@@ -2416,6 +2430,10 @@ public class AudioManager {
* @deprecated use {@link #requestAudioFocus(AudioFocusRequest, AudioPolicy)}
*/
@SystemApi
+ @RequiresPermission(anyOf= {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
@@ -2474,6 +2492,7 @@ public class AudioManager {
* @throws IllegalArgumentException when trying to lock focus without an AudioPolicy
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
if (afr == null) {
throw new NullPointerException("Illegal null AudioFocusRequest");
@@ -2571,6 +2590,7 @@ public class AudioManager {
* @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
@NonNull AudioPolicy ap) {
if (afi == null) {
@@ -2622,6 +2642,8 @@ public class AudioManager {
* @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
*/
@SystemApi
+ @SuppressLint("Doclava125") // no permission enforcement, but only "undoes" what would have been
+ // done by a matching requestAudioFocus
public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
int status = AUDIOFOCUS_REQUEST_FAILED;
unregisterAudioFocusRequest(l);
@@ -3833,6 +3855,7 @@ public class AudioManager {
* @hide
*/
@SystemApi
+ @SuppressLint("Doclava125") // FIXME is this still used?
public boolean isHdmiSystemAudioSupported() {
try {
return getService().isHdmiSystemAudioSupported();
diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java
index 14485d3c43a3..e6aea99ef50b 100644
--- a/media/java/android/media/MediaDescription.java
+++ b/media/java/android/media/MediaDescription.java
@@ -220,6 +220,33 @@ public class MediaDescription implements Parcelable {
}
@Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+
+ if (!(o instanceof MediaDescription)){
+ return false;
+ }
+
+ final MediaDescription d = (MediaDescription) o;
+
+ if (!String.valueOf(mTitle).equals(String.valueOf(d.mTitle))) {
+ return false;
+ }
+
+ if (!String.valueOf(mSubtitle).equals(String.valueOf(d.mSubtitle))) {
+ return false;
+ }
+
+ if (!String.valueOf(mDescription).equals(String.valueOf(d.mDescription))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
public String toString() {
return mTitle + ", " + mSubtitle + ", " + mDescription;
}
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index fc4d15fad5ca..35937de287a3 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -270,7 +270,7 @@ public class MediaFile {
addFileType("PDF", FILE_TYPE_PDF, "application/pdf");
addFileType("DOC", FILE_TYPE_MS_WORD, "application/msword", MtpConstants.FORMAT_MS_WORD_DOCUMENT, true);
addFileType("XLS", FILE_TYPE_MS_EXCEL, "application/vnd.ms-excel", MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, true);
- addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/mspowerpoint", MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, true);
+ addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/vnd.ms-powerpoint", MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, true);
addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac", MtpConstants.FORMAT_FLAC, true);
addFileType("ZIP", FILE_TYPE_ZIP, "application/zip");
addFileType("MPG", FILE_TYPE_MP2PS, "video/mp2p");
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 44bd252a349e..1291dfb59d2c 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -49,6 +49,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.Objects;
/**
* Allows interaction with media controllers, volume keys, media buttons, and
@@ -1291,6 +1292,28 @@ public final class MediaSession {
"Description=" + mDescription +
", Id=" + mId + " }";
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+
+ if (!(o instanceof QueueItem)) {
+ return false;
+ }
+
+ final QueueItem item = (QueueItem) o;
+ if (mId != item.mId) {
+ return false;
+ }
+
+ if (!Objects.equals(mDescription, item.mDescription)) {
+ return false;
+ }
+
+ return true;
+ }
}
private static final class Command {
diff --git a/packages/BackupRestoreConfirmation/res/values-mr/strings.xml b/packages/BackupRestoreConfirmation/res/values-mr/strings.xml
index 5578f4520ebc..ea3b2f05e8f3 100644
--- a/packages/BackupRestoreConfirmation/res/values-mr/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-mr/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"पूर्ण बॅकअप"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"पूर्ण पुनर्संचयन"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"कनेक्‍ट केलेल्‍या डेस्‍कटॉप संगणकावरील सर्व डेटाच्‍या पूर्ण बॅकअपची विनंती केली गेली आहे. आपण असे होण्यासाठी अनुमती देऊ इच्‍छिता?\n\nआपण स्‍वत: बॅकअपची विनंती केली नसल्‍यास, कार्य पुढे सुरु राहण्‍यास अनुमती देऊ नका."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"कनेक्‍ट केलेल्‍या डेस्‍कटॉप काँप्युटरवरील सर्व डेटाच्‍या पूर्ण बॅकअपची विनंती केली गेली आहे. आपण असे होण्यासाठी अनुमती देऊ इच्‍छिता?\n\nआपण स्‍वत: बॅकअपची विनंती केली नसल्‍यास, कार्य पुढे सुरु राहण्‍यास अनुमती देऊ नका."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"माझ्‍या डेटाचा बॅकअप घ्‍या"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"बॅकअप घेऊ नका"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"कनेक्‍ट केलेल्‍या डेस्‍कटॉप संगणकावरील सर्व डेटाच्या पूर्ण पुनर्संचयनाची विनंती केली गेली आहे. आपण असे होण्यासाठी अनुमती देऊ इच्‍छिता?\n\nआपण स्‍वत: पुनर्संचयनाची विनंती केली नसल्‍यास, कार्य पुढे सुरु राहण्‍यास अनुमती देऊ नका. हे आपल्‍या डिव्‍हाइसवरील कोणत्याही वर्तमान डेटास पुनर्स्‍थित करेल!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"कनेक्‍ट केलेल्‍या डेस्‍कटॉप काँप्युटरवरील सर्व डेटाच्या पूर्ण पुनर्संचयनाची विनंती केली गेली आहे. आपण असे होण्यासाठी अनुमती देऊ इच्‍छिता?\n\nआपण स्‍वत: पुनर्संचयनाची विनंती केली नसल्‍यास, कार्य पुढे सुरु राहण्‍यास अनुमती देऊ नका. हे आपल्‍या डिव्‍हाइसवरील कोणत्याही वर्तमान डेटास पुनर्स्‍थित करेल!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"माझा डेटा पुनर्संचयित करा"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"पुनर्संचयित करू नका"</string>
<string name="current_password_text" msgid="8268189555578298067">"कृपया आपला वर्तमान बॅकअप संकेतशब्‍द खाली प्रविष्‍ट करा:"</string>
diff --git a/packages/SystemUI/res/layout/global_actions_item.xml b/packages/SystemUI/res/layout/global_actions_item.xml
index e3a488c20c3f..0d735e7fff33 100644
--- a/packages/SystemUI/res/layout/global_actions_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_item.xml
@@ -25,8 +25,8 @@
android:minHeight="92dp"
android:gravity="center"
android:orientation="vertical"
- android:paddingEnd="8dip"
- android:paddingStart="8dip">
+ android:paddingEnd="0dip"
+ android:paddingStart="0dip">
<ImageView
android:id="@*android:id/icon"
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 27cfdc133bc8..0b20b105617d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -89,6 +89,9 @@ public class TaskStackViewScroller {
mContext = context;
mCb = cb;
mScroller = new OverScroller(context);
+ if (Recents.getConfiguration().isLowRamDevice) {
+ mScroller.setFriction(0.06f);
+ }
mLayoutAlgorithm = layoutAlgorithm;
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
}
@@ -195,7 +198,6 @@ public class TaskStackViewScroller {
return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
}
-
/**
* Scrolls the closest task and snaps into place. Only used in recents for low ram devices.
* @param velocity of scroll
@@ -208,19 +210,30 @@ public class TaskStackViewScroller {
|| stackScroll > mLayoutAlgorithm.mMaxScrollP) {
return;
}
-
TaskStackLowRamLayoutAlgorithm algorithm = mLayoutAlgorithm.mTaskStackLowRamLayoutAlgorithm;
- float newScrollP = algorithm.getClosestTaskP(stackScroll,
- mLayoutAlgorithm.mNumStackTasks, velocity);
- float flingThreshold = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
+ float flingThreshold = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
if (Math.abs(velocity) > flingThreshold) {
+ int minY = algorithm.percentageToScroll(mLayoutAlgorithm.mMinScrollP);
+ int maxY = algorithm.percentageToScroll(mLayoutAlgorithm.mMaxScrollP);
+
+ // Calculate the fling and snap to closest task from final y position, computeScroll()
+ // never runs when cancelled with animateScroll() and the overscroll is not calculated
+ // here
+ fling(0 /* downScrollP */, 0 /* downY */, algorithm.percentageToScroll(stackScroll),
+ -velocity, minY, maxY, 0 /* overscroll */);
+ float pos = algorithm.scrollToPercentage(mScroller.getFinalY());
+
+ float newScrollP = algorithm.getClosestTaskP(pos, mLayoutAlgorithm.mNumStackTasks,
+ velocity);
ValueAnimator animator = ObjectAnimator.ofFloat(stackScroll, newScrollP);
mFlingAnimationUtils.apply(animator, algorithm.percentageToScroll(stackScroll),
algorithm.percentageToScroll(newScrollP), velocity);
animateScroll(newScrollP, (int) animator.getDuration(), animator.getInterpolator(),
null /* postRunnable */);
} else {
+ float newScrollP = algorithm.getClosestTaskP(stackScroll,
+ mLayoutAlgorithm.mNumStackTasks, velocity);
animateScroll(newScrollP, 300, Interpolators.ACCELERATE_DECELERATE,
null /* postRunnable */);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 71f699c8da54..ddc819d39d5e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -655,6 +655,21 @@ public final class AutofillManagerService extends SystemService {
}
@Override
+ public void onPendingSaveUi(int operation, IBinder token) {
+ Preconditions.checkNotNull(token, "token");
+ Preconditions.checkArgument(operation == AutofillManager.PENDING_UI_OPERATION_CANCEL
+ || operation == AutofillManager.PENDING_UI_OPERATION_RESTORE,
+ "invalid operation: %d", operation);
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(
+ UserHandle.getCallingUserId());
+ if (service != null) {
+ service.onPendingSaveUi(operation, token);
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 751c0547afd6..20ccee286fbc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -41,7 +41,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutofillService;
@@ -52,10 +51,12 @@ import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.DebugUtils;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
@@ -233,26 +234,6 @@ final class AutofillManagerServiceImpl {
}
}
- /**
- * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app.
- */
- void requestSaveForUserLocked(IBinder activityToken) {
- if (!isEnabled()) {
- return;
- }
-
- final int numSessions = mSessions.size();
- for (int i = 0; i < numSessions; i++) {
- final Session session = mSessions.valueAt(i);
- if (session.getActivityTokenLocked().equals(activityToken)) {
- session.callSaveLocked();
- return;
- }
- }
-
- Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
- }
-
boolean addClientLocked(IAutoFillManagerClient client) {
if (mClients == null) {
mClients = new RemoteCallbackList<>();
@@ -290,6 +271,7 @@ final class AutofillManagerServiceImpl {
if (!isEnabled()) {
return 0;
}
+ if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags);
// Occasionally clean up abandoned sessions
pruneAbandonedSessionsLocked();
@@ -461,6 +443,25 @@ final class AutofillManagerServiceImpl {
}
}
+ void onPendingSaveUi(int operation, @NonNull IBinder token) {
+ if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
+ synchronized (mLock) {
+ final int sessionCount = mSessions.size();
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ final Session session = mSessions.valueAt(i);
+ if (session.isSaveUiPendingForToken(token)) {
+ session.onPendingSaveUi(operation, token);
+ return;
+ }
+ }
+ }
+ if (sDebug) {
+ Slog.d(TAG, "No pending Save UI for token " + token + " and operation "
+ + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_",
+ operation));
+ }
+ }
+
void destroyLocked() {
if (sVerbose) Slog.v(TAG, "destroyLocked()");
@@ -622,8 +623,12 @@ final class AutofillManagerServiceImpl {
}
void destroySessionsLocked() {
+ if (mSessions.size() == 0) {
+ mUi.destroyAll(AutofillManager.NO_SESSION, null, null);
+ return;
+ }
while (mSessions.size() > 0) {
- mSessions.valueAt(0).removeSelfLocked();
+ mSessions.valueAt(0).forceRemoveSelfLocked();
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f8fb13a54115..95db6039b696 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -77,6 +77,7 @@ import com.android.internal.os.HandlerCaller;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.autofill.ui.PendingUi;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -164,10 +165,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private boolean mDestroyed;
- /** Whether the session is currently saving */
+ /** Whether the session is currently saving. */
@GuardedBy("mLock")
private boolean mIsSaving;
+ /**
+ * Helper used to handle state of Save UI when it must be hiding to show a custom description
+ * link and later recovered.
+ */
+ @GuardedBy("mLock")
+ private PendingUi mPendingSaveUi;
/**
* Receiver of assist data from the app's {@link Activity}.
@@ -701,7 +708,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mHandlerCaller.getHandler().post(() -> {
try {
synchronized (mLock) {
- mClient.startIntentSender(intentSender);
+ mClient.startIntentSender(intentSender, null);
}
} catch (RemoteException e) {
Slog.e(TAG, "Error launching auth intent", e);
@@ -964,8 +971,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!");
mService.setSaveShown(id);
+ final IAutoFillManagerClient client = getClient();
+ mPendingSaveUi = new PendingUi(mActivityToken);
getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo,
- valueFinder, mPackageName, this);
+ valueFinder, mPackageName, this, mPendingSaveUi, id, client);
+ if (client != null) {
+ try {
+ client.setSaveUiState(id, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client to set save UI state to shown: " + e);
+ }
+ }
mIsSaving = true;
return false;
}
@@ -1246,7 +1262,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Remove the UI if the ViewState has changed.
if (mCurrentViewId != viewState.id) {
- hideFillUiIfOwnedByMe();
+ mUi.hideFillUi(this);
mCurrentViewId = viewState.id;
}
@@ -1256,7 +1272,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
case ACTION_VIEW_EXITED:
if (mCurrentViewId == viewState.id) {
if (sVerbose) Slog.d(TAG, "Exiting view " + id);
- hideFillUiIfOwnedByMe();
+ mUi.hideFillUi(this);
mCurrentViewId = null;
}
break;
@@ -1396,7 +1412,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private void processResponseLocked(@NonNull FillResponse newResponse, int flags) {
// Make sure we are hiding the UI which will be shown
// only if handling the current response requires it.
- hideAllUiIfOwnedByMe();
+ mUi.hideAll(this);
final int requestId = newResponse.getRequestId();
if (sVerbose) {
@@ -1583,6 +1599,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving);
+ pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi);
for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
entry.getValue().dump(prefix2, pw);
@@ -1644,7 +1661,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (!ids.isEmpty()) {
if (waitingDatasetAuth) {
- hideFillUiIfOwnedByMe();
+ mUi.hideFillUi(this);
}
if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
@@ -1664,38 +1681,65 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ /**
+ * Cleans up this session.
+ *
+ * <p>Typically called in 2 scenarios:
+ *
+ * <ul>
+ * <li>When the session naturally finishes (i.e., from {@link #removeSelfLocked()}.
+ * <li>When the service hosting the session is finished (for example, because the user
+ * disabled it).
+ * </ul>
+ */
RemoteFillService destroyLocked() {
if (mDestroyed) {
return null;
}
- hideAllUiIfOwnedByMe();
+ mUi.destroyAll(id, getClient(), this);
mUi.clearCallback(this);
mDestroyed = true;
mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
return mRemoteFillService;
}
- private void hideAllUiIfOwnedByMe() {
- mUi.hideAll(this);
- }
+ /**
+ * Cleans up this session and remove it from the service always, even if it does have a pending
+ * Save UI.
+ */
+ void forceRemoveSelfLocked() {
+ if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
- private void hideFillUiIfOwnedByMe() {
- mUi.hideFillUi(this);
+ mPendingSaveUi = null;
+ removeSelfLocked();
+ mUi.destroyAll(id, getClient(), this);
}
+ /**
+ * Thread-safe version of {@link #removeSelfLocked()}.
+ */
private void removeSelf() {
synchronized (mLock) {
removeSelfLocked();
}
}
+ /**
+ * Cleans up this session and remove it from the service, but but only if it does not have a
+ * pending Save UI.
+ */
void removeSelfLocked() {
- if (sVerbose) Slog.v(TAG, "removeSelfLocked()");
+ if (sVerbose) Slog.v(TAG, "removeSelfLocked(): " + mPendingSaveUi);
if (mDestroyed) {
Slog.w(TAG, "Call to Session#removeSelfLocked() rejected - session: "
+ id + " destroyed");
return;
}
+ if (isSaveUiPending()) {
+ Slog.i(TAG, "removeSelfLocked() ignored, waiting for pending save ui");
+ return;
+ }
+
final RemoteFillService remoteFillService = destroyLocked();
mService.removeSessionLocked(id);
if (remoteFillService != null) {
@@ -1703,6 +1747,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ void onPendingSaveUi(int operation, @NonNull IBinder token) {
+ getUiForShowing().onPendingSaveUi(operation, token);
+ }
+
+ /**
+ * Checks whether this session is hiding the Save UI to handle a custom description link for
+ * a specific {@code token} created by {@link PendingUi#PendingUi(IBinder)}.
+ */
+ boolean isSaveUiPendingForToken(@NonNull IBinder token) {
+ return isSaveUiPending() && token.equals(mPendingSaveUi.getToken());
+ }
+
+ /**
+ * Checks whether this session is hiding the Save UI to handle a custom description link.
+ */
+ private boolean isSaveUiPending() {
+ return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
+ }
+
private int getLastResponseIndexLocked() {
// The response ids are monotonically increasing so
// we just find the largest id which is the last. We
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 67ee1858f583..7febf8305d57 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -25,6 +25,8 @@ import android.content.IntentSender;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
@@ -33,6 +35,7 @@ import android.text.TextUtils;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import android.widget.Toast;
@@ -143,7 +146,6 @@ public final class AutoFillUI {
if (callback != mCallback) {
return;
}
- hideSaveUiUiThread(callback);
if (mFillUi != null) {
mFillUi.setFilterText(filterText);
}
@@ -245,7 +247,8 @@ public final class AutoFillUI {
*/
public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info,
@NonNull ValueFinder valueFinder, @NonNull String packageName,
- @NonNull AutoFillUiCallback callback) {
+ @NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingUi,
+ int sessionId, @Nullable IAutoFillManagerClient client) {
if (sVerbose) Slog.v(TAG, "showSaveUi() for " + packageName + ": " + info);
int numIds = 0;
numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
@@ -260,21 +263,22 @@ public final class AutoFillUI {
return;
}
hideAllUiThread(callback);
- mSaveUi = new SaveUi(mContext, providerLabel, info, valueFinder, mOverlayControl,
- new SaveUi.OnSaveListener() {
+ mSaveUi = new SaveUi(mContext, pendingUi, providerLabel, info, valueFinder,
+ mOverlayControl, client, new SaveUi.OnSaveListener() {
@Override
public void onSave() {
log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
- hideSaveUiUiThread(callback);
+ hideSaveUiUiThread(mCallback);
if (mCallback != null) {
mCallback.save();
}
+ destroySaveUiUiThread(sessionId, client);
}
@Override
public void onCancel(IntentSender listener) {
log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
- hideSaveUiUiThread(callback);
+ hideSaveUiUiThread(mCallback);
if (listener != null) {
try {
listener.sendIntent(mContext, 0, null, null, null);
@@ -286,6 +290,7 @@ public final class AutoFillUI {
if (mCallback != null) {
mCallback.cancelSave();
}
+ destroySaveUiUiThread(sessionId, client);
}
@Override
@@ -304,12 +309,33 @@ public final class AutoFillUI {
}
/**
+ * Executes an operation in the pending save UI, if any.
+ */
+ public void onPendingSaveUi(int operation, @NonNull IBinder token) {
+ mHandler.post(() -> {
+ if (mSaveUi != null) {
+ mSaveUi.onPendingUi(operation, token);
+ } else {
+ Slog.w(TAG, "onPendingSaveUi(" + operation + "): no save ui");
+ }
+ });
+ }
+
+ /**
* Hides all UI affordances.
*/
public void hideAll(@Nullable AutoFillUiCallback callback) {
mHandler.post(() -> hideAllUiThread(callback));
}
+ /**
+ * Destroy all UI affordances.
+ */
+ public void destroyAll(int sessionId, @Nullable IAutoFillManagerClient client,
+ @Nullable AutoFillUiCallback callback) {
+ mHandler.post(() -> destroyAllUiThread(sessionId, client, callback));
+ }
+
public void dump(PrintWriter pw) {
pw.println("Autofill UI");
final String prefix = " ";
@@ -343,12 +369,41 @@ public final class AutoFillUI {
+ ", mCallback=" + mCallback);
}
if (mSaveUi != null && (callback == null || callback == mCallback)) {
- mSaveUi.destroy();
- mSaveUi = null;
+ mSaveUi.hide();
}
}
@android.annotation.UiThread
+ private void destroySaveUiUiThread(int sessionId, @Nullable IAutoFillManagerClient client) {
+ if (mSaveUi == null) {
+ // Calling destroySaveUiUiThread() twice is normal - it usually happens when the
+ // first call is made after the SaveUI is hidden and the second when the session is
+ // finished.
+ if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): already destroyed");
+ return;
+ }
+
+ if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): id=" + sessionId);
+ mSaveUi.destroy();
+ mSaveUi = null;
+ if (client != null) {
+ try {
+ if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): notifying client");
+ client.setSaveUiState(sessionId, false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client to set save UI state to hidden: " + e);
+ }
+ }
+ }
+
+ @android.annotation.UiThread
+ private void destroyAllUiThread(int sessionId, @Nullable IAutoFillManagerClient client,
+ @Nullable AutoFillUiCallback callback) {
+ hideFillUiUiThread(callback);
+ destroySaveUiUiThread(sessionId, client);
+ }
+
+ @android.annotation.UiThread
private void hideAllUiThread(@Nullable AutoFillUiCallback callback) {
hideFillUiUiThread(callback);
hideSaveUiUiThread(callback);
diff --git a/services/autofill/java/com/android/server/autofill/ui/PendingUi.java b/services/autofill/java/com/android/server/autofill/ui/PendingUi.java
new file mode 100644
index 000000000000..87263ed61ee9
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/PendingUi.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 com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.util.DebugUtils;
+
+/**
+ * Helper class used to handle a pending Autofill affordance such as the Save UI.
+ *
+ * <p>This class is not thread safe.
+ */
+// NOTE: this class could be an interface implemented by Session, but that would make it harder
+// to move the Autofill UI logic to a different process.
+public final class PendingUi {
+
+ public static final int STATE_CREATED = 1;
+ public static final int STATE_PENDING = 2;
+ public static final int STATE_FINISHED = 4;
+
+ private final IBinder mToken;
+ private int mState;
+
+ /**
+ * Default constructor.
+ *
+ * @param token token used to identify this pending UI.
+ */
+ public PendingUi(@NonNull IBinder token) {
+ mToken = token;
+ mState = STATE_CREATED;
+ }
+
+ /**
+ * Gets the token used to identify this pending UI.
+ */
+ @NonNull
+ public IBinder getToken() {
+ return mToken;
+ }
+
+ /**
+ * Sets the current lifecycle state.
+ */
+ public void setState(int state) {
+ mState = state;
+ }
+
+ /**
+ * Gets the current lifecycle state.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Determines whether the given token matches the token used to identify this pending UI.
+ */
+ public boolean matches(IBinder token) {
+ return mToken.equals(token);
+ }
+
+ @Override
+ public String toString() {
+ return "PendingUi: [token=" + mToken + ", state="
+ + DebugUtils.flagsToString(PendingUi.class, "STATE_", mState) + "]";
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 3727c6eb0e6c..67c1b8cdf45a 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -21,9 +21,14 @@ import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.app.Dialog;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentSender;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.service.autofill.CustomDescription;
import android.service.autofill.SaveInfo;
import android.service.autofill.ValueFinder;
@@ -31,15 +36,17 @@ import android.text.Html;
import android.util.ArraySet;
import android.util.Slog;
import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManagerClient;
import android.widget.RemoteViews;
import android.widget.ScrollView;
import android.widget.TextView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
import com.android.internal.R;
import com.android.server.UiThread;
@@ -109,12 +116,15 @@ final class SaveUi {
private final CharSequence mTitle;
private final CharSequence mSubTitle;
+ private final PendingUi mPendingUi;
private boolean mDestroyed;
- SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
+ SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
+ @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
@NonNull ValueFinder valueFinder, @NonNull OverlayControl overlayControl,
- @NonNull OnSaveListener listener) {
+ @NonNull IAutoFillManagerClient client, @NonNull OnSaveListener listener) {
+ mPendingUi= pendingUi;
mListener = new OneTimeListener(listener);
mOverlayControl = overlayControl;
@@ -171,8 +181,49 @@ final class SaveUi {
final RemoteViews presentation = customDescription.getPresentation(valueFinder);
if (presentation != null) {
+ final RemoteViews.OnClickHandler handler = new RemoteViews.OnClickHandler() {
+ @Override
+ public boolean onClickHandler(View view, PendingIntent pendingIntent,
+ Intent intent) {
+ // We need to hide the Save UI before launching the pending intent, and
+ // restore back it once the activity is finished, and that's achieved by
+ // adding a custom extra in the activity intent.
+ if (pendingIntent != null) {
+ if (intent == null) {
+ Slog.w(TAG,
+ "remote view on custom description does not have intent");
+ return false;
+ }
+ if (!pendingIntent.isActivity()) {
+ Slog.w(TAG, "ignoring custom description pending intent that's not "
+ + "for an activity: " + pendingIntent);
+ return false;
+ }
+ if (sVerbose) {
+ Slog.v(TAG,
+ "Intercepting custom description intent: " + intent);
+ }
+ final IBinder token = mPendingUi.getToken();
+ intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
+ try {
+ client.startIntentSender(pendingIntent.getIntentSender(),
+ intent);
+ mPendingUi.setState(PendingUi.STATE_PENDING);
+ if (sDebug) {
+ Slog.d(TAG, "hiding UI until restored with token " + token);
+ }
+ hide();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "error triggering pending intent: " + intent);
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
try {
- final View customSubtitleView = presentation.apply(context, null);
+ final View customSubtitleView = presentation.apply(context, null, handler);
subtitleContainer = view.findViewById(R.id.autofill_save_custom_subtitle);
subtitleContainer.addView(customSubtitleView);
subtitleContainer.setVisibility(View.VISIBLE);
@@ -202,7 +253,7 @@ final class SaveUi {
} else {
noButton.setText(R.string.autofill_save_no);
}
- View.OnClickListener cancelListener =
+ final View.OnClickListener cancelListener =
(v) -> mListener.onCancel(info.getNegativeActionListener());
noButton.setOnClickListener(cancelListener);
@@ -212,6 +263,9 @@ final class SaveUi {
mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel);
mDialog.setContentView(view);
+ // Dialog can be dismissed when touched outside.
+ mDialog.setOnDismissListener((d) -> mListener.onCancel(info.getNegativeActionListener()));
+
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -227,9 +281,50 @@ final class SaveUi {
params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title);
params.windowAnimations = R.style.AutofillSaveAnimation;
+ show();
+ }
+
+ /**
+ * Update the pending UI, if any.
+ *
+ * @param operation how to update it.
+ * @param token token associated with the pending UI - if it doesn't match the pending token,
+ * the operation will be ignored.
+ */
+ void onPendingUi(int operation, @NonNull IBinder token) {
+ if (!mPendingUi.matches(token)) {
+ Slog.w(TAG, "restore(" + operation + "): got token " + token + " instead of "
+ + mPendingUi.getToken());
+ return;
+ }
+ switch (operation) {
+ case AutofillManager.PENDING_UI_OPERATION_RESTORE:
+ if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token);
+ show();
+ break;
+ case AutofillManager.PENDING_UI_OPERATION_CANCEL:
+ if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token);
+ hide();
+ break;
+ default:
+ Slog.w(TAG, "restore(): invalid operation " + operation);
+ }
+ mPendingUi.setState(PendingUi.STATE_FINISHED);
+ }
+
+ private void show() {
Slog.i(TAG, "Showing save dialog: " + mTitle);
mDialog.show();
mOverlayControl.hideOverlays();
+ }
+
+ void hide() {
+ if (sVerbose) Slog.v(TAG, "Hiding save dialog.");
+ try {
+ mDialog.hide();
+ } finally {
+ mOverlayControl.showOverlays();
+ }
}
void destroy() {
@@ -238,7 +333,6 @@ final class SaveUi {
throwIfDestroyed();
mListener.onDestroy();
mHandler.removeCallbacksAndMessages(mListener);
- if (sVerbose) Slog.v(TAG, "destroy(): dismissing dialog");
mDialog.dismiss();
mDestroyed = true;
} finally {
@@ -260,6 +354,7 @@ final class SaveUi {
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("title: "); pw.println(mTitle);
pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle);
+ pw.print(prefix); pw.print("pendingUi: "); pw.println(mPendingUi);
final View view = mDialog.getWindow().getDecorView();
final int[] loc = view.getLocationOnScreen();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index adf536bbf487..17292b449072 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2011,16 +2011,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
break;
}
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
- if (VDBG) {
- log("Update of LinkProperties for " + nai.name() +
- "; created=" + nai.created +
- "; everConnected=" + nai.everConnected);
- }
- LinkProperties oldLp = nai.linkProperties;
- synchronized (nai) {
- nai.linkProperties = (LinkProperties)msg.obj;
- }
- if (nai.everConnected) updateLinkProperties(nai, oldLp);
+ handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
break;
}
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2269,7 +2260,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
- maybeStopClat(nai);
+ nai.maybeStopClat();
synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
@@ -4383,7 +4374,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateDnses(newLp, oldLp, netId);
// Start or stop clat accordingly to network state.
- updateClat(networkAgent);
+ networkAgent.updateClat(mNetd);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4398,32 +4389,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
- private void updateClat(NetworkAgentInfo nai) {
- if (Nat464Xlat.requiresClat(nai)) {
- maybeStartClat(nai);
- } else {
- maybeStopClat(nai);
- }
- }
-
- /** Ensure clat has started for this network. */
- private void maybeStartClat(NetworkAgentInfo nai) {
- if (nai.clatd != null && nai.clatd.isStarted()) {
- return;
- }
- nai.clatd = new Nat464Xlat(mNetd, mTrackerHandler, nai);
- nai.clatd.start();
- }
-
- /** Ensure clat has stopped for this network. */
- private void maybeStopClat(NetworkAgentInfo nai) {
- if (nai.clatd == null) {
- return;
- }
- nai.clatd.stop();
- nai.clatd = null;
- }
-
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interaces. Checking for
// marks on unsupported interfaces is harmless.
@@ -4658,6 +4623,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
+ if (VDBG) {
+ log("Update of LinkProperties for " + nai.name() +
+ "; created=" + nai.created +
+ "; everConnected=" + nai.everConnected);
+ }
+ LinkProperties oldLp = nai.linkProperties;
+ synchronized (nai) {
+ nai.linkProperties = newLp;
+ }
+ if (nai.everConnected) {
+ updateLinkProperties(nai, oldLp);
+ }
+ }
+
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 1921ada70cac..ded202db8713 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1575,7 +1575,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
void setVisibility(boolean visible) {
- mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+ mWindowContainerController.setVisibility(visible, visibleIgnoringKeyguard,
+ mDeferHidingClient);
mStackSupervisor.mActivityMetricsLogger.notifyVisibilityChanged(this, visible);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4202d66f0495..89d62d19214d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2597,6 +2597,16 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// the screen based on the new activity order.
boolean notUpdated = true;
if (mStackSupervisor.isFocusedStack(this)) {
+
+ // We have special rotation behavior when Keyguard is locked. Make sure all activity
+ // visibilities are set correctly as well as the transition is updated if needed to
+ // get the correct rotation behavior.
+ // TODO: Remove this once visibilities are set correctly immediately when starting
+ // an activity.
+ if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
+ 0 /* configChanges */, false /* preserveWindows */);
+ }
final Configuration config = mWindowManager.updateOrientationFromAppTokens(
mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
next.mayFreezeScreenLocked(next.app) ? next.appToken : null, mDisplayId);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 32f595084d63..16591dab0c91 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1269,6 +1269,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.app = app;
+ if (mKeyguardController.isKeyguardLocked()) {
+ r.notifyUnknownVisibilityLaunched();
+ }
+
// Have the window manager re-evaluate the orientation of the screen based on the new
// activity order. Note that as a result of this, it can call back into the activity
// manager with a new orientation. We don't care about that, because the activity is
@@ -1295,9 +1299,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.setVisibility(true);
}
- if (mKeyguardController.isKeyguardLocked()) {
- r.notifyUnknownVisibilityLaunched();
- }
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 503810683fdf..f88f779b1676 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -16,6 +16,11 @@
package com.android.server.audio;
+import com.android.server.audio.AudioServiceEvents.ForceUseEvent;
+import com.android.server.audio.AudioServiceEvents.PhoneStateEvent;
+import com.android.server.audio.AudioServiceEvents.VolumeEvent;
+import com.android.server.audio.AudioServiceEvents.WiredDevConnectEvent;
+
import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
@@ -1310,6 +1315,9 @@ public class AudioService extends IAudioService.Stub
+ ", flags=" + flags + ", caller=" + caller
+ ", volControlStream=" + mVolumeControlStream
+ ", userSelect=" + mUserSelectedVolumeControlStream);
+ mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,
+ direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
+ .append("/").append(caller).append(" uid:").append(uid).toString()));
final int streamType;
if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
@@ -1359,6 +1367,8 @@ public class AudioService extends IAudioService.Stub
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
return;
}
+ mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
+ direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
Binder.getCallingUid());
}
@@ -1675,6 +1685,8 @@ public class AudioService extends IAudioService.Stub
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
+ mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
+ index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
Binder.getCallingUid());
}
@@ -4017,7 +4029,7 @@ public class AudioService extends IAudioService.Stub
/*
* A class just for packaging up a set of connection parameters.
*/
- private class WiredDeviceConnectionState {
+ class WiredDeviceConnectionState {
public final int mType;
public final int mState;
public final String mAddress;
@@ -6372,63 +6384,7 @@ public class AudioService extends IAudioService.Stub
final int LOG_NB_EVENTS_PHONE_STATE = 20;
final int LOG_NB_EVENTS_WIRED_DEV_CONNECTION = 30;
final int LOG_NB_EVENTS_FORCE_USE = 20;
-
- final private static class PhoneStateEvent extends AudioEventLogger.Event {
- final String mPackage;
- final int mPid;
- final int mMode;
-
- PhoneStateEvent(String callingPackage, int pid, int mode) {
- mPackage = callingPackage;
- mPid = pid;
- mMode = mode;
- }
-
- @Override
- public String eventToString() {
- return new StringBuilder("setMode(").append(AudioSystem.modeToString(mMode))
- .append(") from package=").append(mPackage)
- .append(" pid=").append(mPid).toString();
- }
- }
-
- final private static class WiredDevConnectEvent extends AudioEventLogger.Event {
- final WiredDeviceConnectionState mState;
-
- WiredDevConnectEvent(WiredDeviceConnectionState state) {
- mState = state;
- }
-
- @Override
- public String eventToString() {
- return new StringBuilder("setWiredDeviceConnectionState(")
- .append(" type:").append(Integer.toHexString(mState.mType))
- .append(" state:").append(AudioSystem.deviceStateToString(mState.mState))
- .append(" addr:").append(mState.mAddress)
- .append(" name:").append(mState.mName)
- .append(") from ").append(mState.mCaller).toString();
- }
- }
-
- final private static class ForceUseEvent extends AudioEventLogger.Event {
- final int mUsage;
- final int mConfig;
- final String mReason;
-
- ForceUseEvent(int usage, int config, String reason) {
- mUsage = usage;
- mConfig = config;
- mReason = reason;
- }
-
- @Override
- public String eventToString() {
- return new StringBuilder("setForceUse(")
- .append(AudioSystem.forceUseUsageToString(mUsage))
- .append(", ").append(AudioSystem.forceUseConfigToString(mConfig))
- .append(") due to ").append(mReason).toString();
- }
- }
+ final int LOG_NB_EVENTS_VOLUME = 40;
final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
"phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
@@ -6442,6 +6398,9 @@ public class AudioService extends IAudioService.Stub
LOG_NB_EVENTS_FORCE_USE,
"force use (logged before setForceUse() is executed)");
+ final private AudioEventLogger mVolumeLogger = new AudioEventLogger(LOG_NB_EVENTS_VOLUME,
+ "volume changes (logged when command received by AudioService)");
+
private static final String[] RINGER_MODE_NAMES = new String[] {
"SILENT",
"VIBRATE",
@@ -6513,12 +6472,15 @@ public class AudioService extends IAudioService.Stub
mRecordMonitor.dump(pw);
+ pw.println("\n");
pw.println("\nEvent logs:");
mModeLogger.dump(pw);
pw.println("\n");
mWiredDevLogger.dump(pw);
pw.println("\n");
mForceUseLogger.dump(pw);
+ pw.println("\n");
+ mVolumeLogger.dump(pw);
}
private static String safeMediaVolumeStateToString(Integer state) {
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
new file mode 100644
index 000000000000..634c8c27a3cc
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -0,0 +1,131 @@
+/*
+ * 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.audio;
+
+import android.media.AudioManager;
+import android.media.AudioSystem;
+
+import com.android.server.audio.AudioService.WiredDeviceConnectionState;
+
+
+public class AudioServiceEvents {
+
+ final static class PhoneStateEvent extends AudioEventLogger.Event {
+ final String mPackage;
+ final int mPid;
+ final int mMode;
+
+ PhoneStateEvent(String callingPackage, int pid, int mode) {
+ mPackage = callingPackage;
+ mPid = pid;
+ mMode = mode;
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("setMode(").append(AudioSystem.modeToString(mMode))
+ .append(") from package=").append(mPackage)
+ .append(" pid=").append(mPid).toString();
+ }
+ }
+
+ final static class WiredDevConnectEvent extends AudioEventLogger.Event {
+ final WiredDeviceConnectionState mState;
+
+ WiredDevConnectEvent(WiredDeviceConnectionState state) {
+ mState = state;
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("setWiredDeviceConnectionState(")
+ .append(" type:").append(Integer.toHexString(mState.mType))
+ .append(" state:").append(AudioSystem.deviceStateToString(mState.mState))
+ .append(" addr:").append(mState.mAddress)
+ .append(" name:").append(mState.mName)
+ .append(") from ").append(mState.mCaller).toString();
+ }
+ }
+
+ final static class ForceUseEvent extends AudioEventLogger.Event {
+ final int mUsage;
+ final int mConfig;
+ final String mReason;
+
+ ForceUseEvent(int usage, int config, String reason) {
+ mUsage = usage;
+ mConfig = config;
+ mReason = reason;
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("setForceUse(")
+ .append(AudioSystem.forceUseUsageToString(mUsage))
+ .append(", ").append(AudioSystem.forceUseConfigToString(mConfig))
+ .append(") due to ").append(mReason).toString();
+ }
+ }
+
+ final static class VolumeEvent extends AudioEventLogger.Event {
+ final static int VOL_ADJUST_SUGG_VOL = 0;
+ final static int VOL_ADJUST_STREAM_VOL = 1;
+ final static int VOL_SET_STREAM_VOL = 2;
+
+ final int mOp;
+ final int mStream;
+ final int mVal1;
+ final int mVal2;
+ final String mCaller;
+
+ VolumeEvent(int op, int stream, int val1, int val2, String caller) {
+ mOp = op;
+ mStream = stream;
+ mVal1 = val1;
+ mVal2 = val2;
+ mCaller = caller;
+ }
+
+ @Override
+ public String eventToString() {
+ switch (mOp) {
+ case VOL_ADJUST_SUGG_VOL:
+ return new StringBuilder("adjustSuggestedStreamVolume(sugg:")
+ .append(AudioSystem.streamToString(mStream))
+ .append(" dir:").append(AudioManager.adjustToString(mVal1))
+ .append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(") from ").append(mCaller)
+ .toString();
+ case VOL_ADJUST_STREAM_VOL:
+ return new StringBuilder("adjustStreamVolume(stream:")
+ .append(AudioSystem.streamToString(mStream))
+ .append(" dir:").append(AudioManager.adjustToString(mVal1))
+ .append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(") from ").append(mCaller)
+ .toString();
+ case VOL_SET_STREAM_VOL:
+ return new StringBuilder("setStreamVolume(stream:")
+ .append(AudioSystem.streamToString(mStream))
+ .append(" index:").append(mVal1)
+ .append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(") from ").append(mCaller)
+ .toString();
+ default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 27426d7bded9..10c8b8b1e0aa 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,25 +16,25 @@
package com.android.server.connectivity;
-import java.net.Inet4Address;
-
import android.net.InterfaceConfiguration;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.NetworkAgent;
import android.net.RouteInfo;
-import android.os.Handler;
-import android.os.Message;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.net.BaseNetworkObserver;
import com.android.internal.util.ArrayUtils;
+import com.android.server.net.BaseNetworkObserver;
+
+import java.net.Inet4Address;
+import java.util.Objects;
/**
- * Class to manage a 464xlat CLAT daemon.
+ * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
+ * from a consistent and unique thread context. It is the responsability of ConnectivityService to
+ * call into this class from its own Handler thread.
*
* @hide
*/
@@ -54,31 +54,21 @@ public class Nat464Xlat extends BaseNetworkObserver {
private final INetworkManagementService mNMService;
- // ConnectivityService Handler for LinkProperties updates.
- private final Handler mHandler;
-
// The network we're running on, and its type.
private final NetworkAgentInfo mNetwork;
- // Internal state variables.
- //
- // The possible states are:
- // - Idle: start() not called. Everything is null.
- // - Starting: start() called. Interfaces are non-null. isStarted() returns true.
- // mIsRunning is false.
- // - Running: start() called, and interfaceLinkStateChanged() told us that mIface is up.
- // mIsRunning is true.
- //
- // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
- // its handler thread must not modify any internal state variables; they are only updated by the
- // interface observers, called on the notification threads.
+ private enum State {
+ IDLE, // start() not called. Base iface and stacked iface names are null.
+ STARTING, // start() called. Base iface and stacked iface names are known.
+ RUNNING; // start() called, and the stacked iface is known to be up.
+ }
+
private String mBaseIface;
private String mIface;
- private boolean mIsRunning;
+ private State mState = State.IDLE;
- public Nat464Xlat(INetworkManagementService nmService, Handler handler, NetworkAgentInfo nai) {
+ public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
mNMService = nmService;
- mHandler = handler;
mNetwork = nai;
}
@@ -99,24 +89,47 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
/**
- * Determines whether clatd is started. Always true, except a) if start has not yet been called,
- * or b) if our interface was removed.
+ * @return true if clatd has been started and has not yet stopped.
+ * A true result corresponds to internal states STARTING and RUNNING.
*/
public boolean isStarted() {
- return mIface != null;
+ return mState != State.IDLE;
+ }
+
+ /**
+ * @return true if clatd has been started but the stacked interface is not yet up.
+ */
+ public boolean isStarting() {
+ return mState == State.STARTING;
+ }
+
+ /**
+ * @return true if clatd has been started and the stacked interface is up.
+ */
+ public boolean isRunning() {
+ return mState == State.RUNNING;
+ }
+
+ /**
+ * Sets internal state.
+ */
+ private void enterStartingState(String baseIface) {
+ mIface = CLAT_PREFIX + baseIface;
+ mBaseIface = baseIface;
+ mState = State.STARTING;
}
/**
- * Clears internal state. Must not be called by ConnectivityService.
+ * Clears internal state.
*/
- private void clear() {
+ private void enterIdleState() {
mIface = null;
mBaseIface = null;
- mIsRunning = false;
+ mState = State.IDLE;
}
/**
- * Starts the clat daemon. Called by ConnectivityService on the handler thread.
+ * Starts the clat daemon.
*/
public void start() {
if (isStarted()) {
@@ -136,45 +149,39 @@ public class Nat464Xlat extends BaseNetworkObserver {
return;
}
- mBaseIface = mNetwork.linkProperties.getInterfaceName();
- if (mBaseIface == null) {
+ String baseIface = mNetwork.linkProperties.getInterfaceName();
+ if (baseIface == null) {
Slog.e(TAG, "startClat: Can't start clat on null interface");
return;
}
- mIface = CLAT_PREFIX + mBaseIface;
- // From now on, isStarted() will return true.
+ // TODO: should we only do this if mNMService.startClatd() succeeds?
+ enterStartingState(baseIface);
Slog.i(TAG, "Starting clatd on " + mBaseIface);
try {
mNMService.startClatd(mBaseIface);
} catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error starting clatd: " + e);
+ Slog.e(TAG, "Error starting clatd on " + mBaseIface, e);
}
}
/**
- * Stops the clat daemon. Called by ConnectivityService on the handler thread.
+ * Stops the clat daemon.
*/
public void stop() {
- if (isStarted()) {
- Slog.i(TAG, "Stopping clatd");
- try {
- mNMService.stopClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error stopping clatd: " + e);
- }
- // When clatd stops and its interface is deleted, interfaceRemoved() will notify
- // ConnectivityService and call clear().
- } else {
- Slog.e(TAG, "clatd: already stopped");
+ if (!isStarted()) {
+ Slog.e(TAG, "stopClat: already stopped or not started");
+ return;
}
- }
- private void updateConnectivityService(LinkProperties lp) {
- Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
- msg.replyTo = mNetwork.messenger;
- Slog.i(TAG, "sending message to ConnectivityService: " + msg);
- msg.sendToTarget();
+ Slog.i(TAG, "Stopping clatd on " + mBaseIface);
+ try {
+ mNMService.stopClatd(mBaseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+ }
+ // When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger
+ // ConnectivityService#handleUpdateLinkProperties and call enterIdleState().
}
/**
@@ -183,16 +190,19 @@ public class Nat464Xlat extends BaseNetworkObserver {
* has no idea that 464xlat is running on top of it.
*/
public void fixupLinkProperties(LinkProperties oldLp) {
- if (mNetwork.clatd != null &&
- mIsRunning &&
- mNetwork.linkProperties != null &&
- !mNetwork.linkProperties.getAllInterfaceNames().contains(mIface)) {
- Slog.d(TAG, "clatd running, updating NAI for " + mIface);
- for (LinkProperties stacked: oldLp.getStackedLinks()) {
- if (mIface.equals(stacked.getInterfaceName())) {
- mNetwork.linkProperties.addStackedLink(stacked);
- break;
- }
+ if (!isRunning()) {
+ return;
+ }
+ LinkProperties lp = mNetwork.linkProperties;
+ if (lp == null || lp.getAllInterfaceNames().contains(mIface)) {
+ return;
+ }
+
+ Slog.d(TAG, "clatd running, updating NAI for " + mIface);
+ for (LinkProperties stacked: oldLp.getStackedLinks()) {
+ if (Objects.equals(mIface, stacked.getInterfaceName())) {
+ lp.addStackedLink(stacked);
+ return;
}
}
}
@@ -238,57 +248,66 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
}
+ /**
+ * Adds stacked link on base link and transitions to RUNNING state.
+ */
+ private void handleInterfaceLinkStateChanged(String iface, boolean up) {
+ if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
+ return;
+ }
+ LinkAddress clatAddress = getLinkAddress(iface);
+ if (clatAddress == null) {
+ Slog.e(TAG, "cladAddress was null for stacked iface " + iface);
+ return;
+ }
+ mState = State.RUNNING;
+ Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
+ mIface, mIface, mBaseIface));
+
+ maybeSetIpv6NdOffload(mBaseIface, false);
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.addStackedLink(makeLinkProperties(clatAddress));
+ mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
+ }
+
+ /**
+ * Removes stacked link on base link and transitions to IDLE state.
+ */
+ private void handleInterfaceRemoved(String iface) {
+ if (!isRunning() || !Objects.equals(mIface, iface)) {
+ return;
+ }
+
+ Slog.i(TAG, "interface " + iface + " removed");
+ // The interface going away likely means clatd has crashed. Ask netd to stop it,
+ // because otherwise when we try to start it again on the same base interface netd
+ // will complain that it's already started.
+ try {
+ mNMService.unregisterObserver(this);
+ // TODO: add STOPPING state to avoid calling stopClatd twice.
+ mNMService.stopClatd(mBaseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+ }
+ maybeSetIpv6NdOffload(mBaseIface, true);
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.removeStackedLink(mIface);
+ enterIdleState();
+ mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
+ }
+
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
- // Called by the InterfaceObserver on its own thread, so can race with stop().
- if (isStarted() && up && mIface.equals(iface)) {
- Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true");
-
- if (!mIsRunning) {
- LinkAddress clatAddress = getLinkAddress(iface);
- if (clatAddress == null) {
- return;
- }
- mIsRunning = true;
- maybeSetIpv6NdOffload(mBaseIface, false);
- LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
- lp.addStackedLink(makeLinkProperties(clatAddress));
- Slog.i(TAG, "Adding stacked link " + mIface + " on top of " + mBaseIface);
- updateConnectivityService(lp);
- }
- }
+ mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); });
}
@Override
public void interfaceRemoved(String iface) {
- if (isStarted() && mIface.equals(iface)) {
- Slog.i(TAG, "interface " + iface + " removed, mIsRunning " + mIsRunning + "->false");
-
- if (mIsRunning) {
- // The interface going away likely means clatd has crashed. Ask netd to stop it,
- // because otherwise when we try to start it again on the same base interface netd
- // will complain that it's already started.
- //
- // Note that this method can be called by the interface observer at the same time
- // that ConnectivityService calls stop(). In this case, the second call to
- // stopClatd() will just throw IllegalStateException, which we'll ignore.
- try {
- mNMService.unregisterObserver(this);
- mNMService.stopClatd(mBaseIface);
- } catch (RemoteException|IllegalStateException e) {
- // Well, we tried.
- }
- maybeSetIpv6NdOffload(mBaseIface, true);
- LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
- lp.removeStackedLink(mIface);
- clear();
- updateConnectivityService(lp);
- }
- }
+ mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); });
}
@Override
public String toString() {
- return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mIsRunning: " + mIsRunning;
+ return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 872923a03256..7c4ef0f0f3b9 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -27,7 +27,9 @@ import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.os.Handler;
+import android.os.INetworkManagementService;
import android.os.Messenger;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -247,9 +249,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
- private final ConnectivityService mConnService;
+ public final ConnectivityService connService;
private final Context mContext;
- private final Handler mHandler;
+ final Handler handler;
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
@@ -261,10 +263,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- mConnService = connService;
+ this.connService = connService;
mContext = context;
- mHandler = handler;
- networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
+ this.handler = handler;
+ networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
}
@@ -430,7 +432,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private boolean ignoreWifiUnvalidationPenalty() {
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
+ boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated;
return isWifi && !avoidBadWifi && everValidated;
}
@@ -514,8 +516,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
}
if (newExpiry > 0) {
- mLingerMessage = mConnService.makeWakeupMessage(
- mContext, mHandler,
+ mLingerMessage = connService.makeWakeupMessage(
+ mContext, handler,
"NETWORK_LINGER_COMPLETE." + network.netId,
EVENT_NETWORK_LINGER_COMPLETE, this);
mLingerMessage.schedule(newExpiry);
@@ -551,6 +553,32 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
+ public void updateClat(INetworkManagementService netd) {
+ if (Nat464Xlat.requiresClat(this)) {
+ maybeStartClat(netd);
+ } else {
+ maybeStopClat();
+ }
+ }
+
+ /** Ensure clat has started for this network. */
+ public void maybeStartClat(INetworkManagementService netd) {
+ if (clatd != null && clatd.isStarted()) {
+ return;
+ }
+ clatd = new Nat464Xlat(netd, this);
+ clatd.start();
+ }
+
+ /** Ensure clat has stopped for this network. */
+ public void maybeStopClat() {
+ if (clatd == null) {
+ return;
+ }
+ clatd.stop();
+ clatd = null;
+ }
+
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index d5d0250f82af..34f1bfaf996f 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -455,6 +455,11 @@ abstract public class ManagedServices {
}
}
+ public void onUserRemoved(int user) {
+ mApproved.remove(user);
+ rebindServices(true);
+ }
+
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a693e9747db2..9a76294cdd9e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -157,6 +157,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -984,12 +985,17 @@ public class NotificationManagerService extends SystemService {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
if (userId != USER_NULL) {
mUserProfiles.updateCache(context);
- readDefaultApprovedServices(userId);
+ if (!mUserProfiles.isManagedProfile(userId)) {
+ readDefaultApprovedServices(userId);
+ }
}
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mZenModeHelper.onUserRemoved(user);
mRankingHelper.onUserRemoved(user);
+ mListeners.onUserRemoved(user);
+ mConditionProviders.onUserRemoved(user);
+ mAssistants.onUserRemoved(user);
savePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
@@ -5567,13 +5573,10 @@ public class NotificationManagerService extends SystemService {
continue;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (hasCompanionDevice(serviceInfo)) {
- notifyNotificationChannelChanged(
- serviceInfo, pkg, user, channel, modificationType);
- }
+ BackgroundThread.getHandler().post(() -> {
+ if (hasCompanionDevice(serviceInfo)) {
+ notifyNotificationChannelChanged(
+ serviceInfo, pkg, user, channel, modificationType);
}
});
}
@@ -5590,13 +5593,10 @@ public class NotificationManagerService extends SystemService {
continue;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (hasCompanionDevice(serviceInfo)) {
- notifyNotificationChannelGroupChanged(
- serviceInfo, pkg, user, group, modificationType);
- }
+ BackgroundThread.getHandler().post(() -> {
+ if (hasCompanionDevice(serviceInfo)) {
+ notifyNotificationChannelGroupChanged(
+ serviceInfo, pkg, user, group, modificationType);
}
});
}
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index b217677479cd..1082eae7df84 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -624,14 +624,25 @@ final class DefaultPermissionGrantPolicy {
grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
}
+ // Home
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
+ PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackageLPr(
+ homeIntent, userId);
+ if (homePackage != null
+ && doesPackageSupportRuntimePermissions(homePackage)) {
+ grantRuntimePermissionsLPw(homePackage, LOCATION_PERMISSIONS, false, userId);
+ }
+
// Watches
if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
// Home application on watches
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+ Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
+ wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
- homeIntent, userId);
+ wearHomeIntent, userId);
if (wearHomePackage != null
&& doesPackageSupportRuntimePermissions(wearHomePackage)) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 63900e0eb067..0867a6c66b70 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -51,6 +51,7 @@ import android.os.UserManager;
import android.os.Vibrator;
import android.os.storage.IStorageManager;
import android.os.storage.IStorageShutdownObserver;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.TimingsTraceLog;
import android.view.WindowManager;
@@ -62,7 +63,9 @@ import com.android.server.pm.PackageManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
public final class ShutdownThread extends Thread {
// constants
@@ -110,6 +113,23 @@ public final class ShutdownThread extends Thread {
private static final TimingsTraceLog SHUTDOWN_TIMINGS_LOG = new TimingsTraceLog(
"ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER);
+ // Metrics that will be reported to tron after reboot
+ private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>();
+
+ // File to use for save metrics
+ private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics";
+
+ // Metrics names to be persisted in shutdown-metrics file
+ private static String METRIC_SYSTEM_SERVER = "shutdown_system_server";
+ private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast";
+ private static String METRIC_AM = "shutdown_activity_manager";
+ private static String METRIC_PM = "shutdown_package_manager";
+ private static String METRIC_RADIOS = "shutdown_radios";
+ private static String METRIC_BT = "shutdown_bt";
+ private static String METRIC_RADIO = "shutdown_radio";
+ private static String METRIC_NFC = "shutdown_nfc";
+ private static String METRIC_SM = "shutdown_storage_manager";
+
private final Object mActionDoneSync = new Object();
private boolean mActionDone;
private Context mContext;
@@ -349,6 +369,7 @@ public final class ShutdownThread extends Thread {
private static void beginShutdownSequence(Context context) {
SHUTDOWN_TIMINGS_LOG.traceBegin("SystemServerShutdown");
+ metricStarted(METRIC_SYSTEM_SERVER);
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
@@ -430,6 +451,7 @@ public final class ShutdownThread extends Thread {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
+ metricStarted(METRIC_SEND_BROADCAST);
SHUTDOWN_TIMINGS_LOG.traceBegin("SendShutdownBroadcast");
Log.i(TAG, "Sending shutdown broadcast...");
@@ -463,9 +485,11 @@ public final class ShutdownThread extends Thread {
sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
}
SHUTDOWN_TIMINGS_LOG.traceEnd(); // SendShutdownBroadcast
+ metricEnded(METRIC_SEND_BROADCAST);
Log.i(TAG, "Shutting down activity manager...");
SHUTDOWN_TIMINGS_LOG.traceBegin("ShutdownActivityManager");
+ metricStarted(METRIC_AM);
final IActivityManager am =
IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
@@ -478,10 +502,12 @@ public final class ShutdownThread extends Thread {
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
}
- SHUTDOWN_TIMINGS_LOG.traceEnd(); // ShutdownActivityManager
+ SHUTDOWN_TIMINGS_LOG.traceEnd();// ShutdownActivityManager
+ metricEnded(METRIC_AM);
Log.i(TAG, "Shutting down package manager...");
SHUTDOWN_TIMINGS_LOG.traceBegin("ShutdownPackageManager");
+ metricStarted(METRIC_PM);
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
@@ -492,14 +518,17 @@ public final class ShutdownThread extends Thread {
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
}
SHUTDOWN_TIMINGS_LOG.traceEnd(); // ShutdownPackageManager
+ metricEnded(METRIC_PM);
// Shutdown radios.
SHUTDOWN_TIMINGS_LOG.traceBegin("ShutdownRadios");
+ metricStarted(METRIC_RADIOS);
shutdownRadios(MAX_RADIO_WAIT_TIME);
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
}
SHUTDOWN_TIMINGS_LOG.traceEnd(); // ShutdownRadios
+ metricEnded(METRIC_RADIOS);
// Shutdown StorageManagerService to ensure media is in a safe state
IStorageShutdownObserver observer = new IStorageShutdownObserver.Stub() {
@@ -511,6 +540,7 @@ public final class ShutdownThread extends Thread {
Log.i(TAG, "Shutting down StorageManagerService");
SHUTDOWN_TIMINGS_LOG.traceBegin("ShutdownStorageManager");
+ metricStarted(METRIC_SM);
// Set initial variables and time out time.
mActionDone = false;
@@ -546,6 +576,7 @@ public final class ShutdownThread extends Thread {
}
}
SHUTDOWN_TIMINGS_LOG.traceEnd(); // ShutdownStorageManager
+ metricEnded(METRIC_SM);
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
@@ -558,6 +589,14 @@ public final class ShutdownThread extends Thread {
rebootOrShutdown(mContext, mReboot, mReason);
}
+ private static void metricStarted(String metricKey) {
+ TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime());
+ }
+
+ private static void metricEnded(String metricKey) {
+ TRON_METRICS.put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey));
+ }
+
private void setRebootProgress(final int progress, final CharSequence message) {
mHandler.post(new Runnable() {
@Override
@@ -590,12 +629,12 @@ public final class ShutdownThread extends Thread {
final IBluetoothManager bluetooth =
IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
- final long nfcShutdownStarted = SystemClock.elapsedRealtime();
try {
nfcOff = nfc == null ||
nfc.getState() == NfcAdapter.STATE_OFF;
if (!nfcOff) {
Log.w(TAG, "Turning off NFC...");
+ metricStarted(METRIC_NFC);
nfc.disable(false); // Don't persist new state
}
} catch (RemoteException ex) {
@@ -603,12 +642,12 @@ public final class ShutdownThread extends Thread {
nfcOff = true;
}
- final long btShutdownStarted = SystemClock.elapsedRealtime();
try {
bluetoothReadyForShutdown = bluetooth == null ||
bluetooth.getState() == BluetoothAdapter.STATE_OFF;
if (!bluetoothReadyForShutdown) {
Log.w(TAG, "Disabling Bluetooth...");
+ metricStarted(METRIC_BT);
bluetooth.disable(mContext.getPackageName(), false); // disable but don't persist new state
}
} catch (RemoteException ex) {
@@ -616,11 +655,11 @@ public final class ShutdownThread extends Thread {
bluetoothReadyForShutdown = true;
}
- final long radioShutdownStarted = SystemClock.elapsedRealtime();
try {
radioOff = phone == null || !phone.needMobileRadioShutdown();
if (!radioOff) {
Log.w(TAG, "Turning off cellular radios...");
+ metricStarted(METRIC_RADIO);
phone.shutdownMobileRadios();
}
} catch (RemoteException ex) {
@@ -653,8 +692,9 @@ public final class ShutdownThread extends Thread {
}
if (bluetoothReadyForShutdown) {
Log.i(TAG, "Bluetooth turned off.");
- SHUTDOWN_TIMINGS_LOG.logDuration("ShutdownBt",
- SystemClock.elapsedRealtime() - btShutdownStarted);
+ metricEnded(METRIC_BT);
+ SHUTDOWN_TIMINGS_LOG
+ .logDuration("ShutdownBt", TRON_METRICS.get(METRIC_BT));
}
}
if (!radioOff) {
@@ -666,8 +706,9 @@ public final class ShutdownThread extends Thread {
}
if (radioOff) {
Log.i(TAG, "Radio turned off.");
- SHUTDOWN_TIMINGS_LOG.logDuration("ShutdownRadio",
- SystemClock.elapsedRealtime() - radioShutdownStarted);
+ metricEnded(METRIC_RADIO);
+ SHUTDOWN_TIMINGS_LOG
+ .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
}
}
if (!nfcOff) {
@@ -679,8 +720,9 @@ public final class ShutdownThread extends Thread {
}
if (nfcOff) {
Log.i(TAG, "NFC turned off.");
- SHUTDOWN_TIMINGS_LOG.logDuration("ShutdownNfc",
- SystemClock.elapsedRealtime() - nfcShutdownStarted);
+ metricEnded(METRIC_NFC);
+ SHUTDOWN_TIMINGS_LOG
+ .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
}
}
@@ -708,7 +750,7 @@ public final class ShutdownThread extends Thread {
/**
* Do not call this directly. Use {@link #reboot(Context, String, boolean)}
- * or {@link #shutdown(Context, boolean)} instead.
+ * or {@link #shutdown(Context, String, boolean)} instead.
*
* @param context Context used to vibrate or null without vibration
* @param reboot true to reboot or false to shutdown
@@ -716,6 +758,8 @@ public final class ShutdownThread extends Thread {
*/
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
SHUTDOWN_TIMINGS_LOG.traceEnd(); // SystemServerShutdown
+ metricEnded(METRIC_SYSTEM_SERVER);
+ saveMetrics(reboot);
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
@@ -742,6 +786,33 @@ public final class ShutdownThread extends Thread {
PowerManagerService.lowLevelShutdown(reason);
}
+ private static void saveMetrics(boolean reboot) {
+ StringBuilder metricValue = new StringBuilder();
+ metricValue.append("reboot:");
+ metricValue.append(reboot ? "y" : "n");
+ final int metricsSize = TRON_METRICS.size();
+ for (int i = 0; i < metricsSize; i++) {
+ final String name = TRON_METRICS.keyAt(i);
+ final long value = TRON_METRICS.valueAt(i);
+ if (value < 0) {
+ Log.e(TAG, "metricEnded wasn't called for " + name);
+ continue;
+ }
+ metricValue.append(',').append(name).append(':').append(value);
+ }
+ File tmp = new File(METRICS_FILE_BASENAME + ".tmp");
+ boolean saved = false;
+ try (FileOutputStream fos = new FileOutputStream(tmp)) {
+ fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8));
+ saved = true;
+ } catch (IOException e) {
+ Log.e(TAG,"Cannot save shutdown metrics", e);
+ }
+ if (saved) {
+ tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt"));
+ }
+ }
+
private void uncrypt() {
Log.i(TAG, "Calling uncrypt and monitoring the progress...");
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index f142ff619884..ebd82e3d7704 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -321,7 +321,8 @@ public class AppWindowContainerController
}
}
- public void setVisibility(boolean visible, boolean deferHidingClient) {
+ public void setVisibility(boolean visible, boolean visibleIgnoringKeyguard,
+ boolean deferHidingClient) {
synchronized(mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
@@ -360,13 +361,16 @@ public class AppWindowContainerController
wtoken.hiddenRequested = !visible;
wtoken.mDeferHidingClient = deferHidingClient;
+ if (!visibleIgnoringKeyguard) {
+ mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
+ }
+
if (!visible) {
// If the app is dead while it was visible, we kept its dead window on screen.
// Now that the app is going invisible, we can remove it. It will be restarted
// if made visible again.
wtoken.removeDeadWindows();
wtoken.setVisibleBeforeClientHidden();
- mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
} else {
if (!mService.mAppTransition.isTransitionSet()
&& mService.mAppTransition.isReady()) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bf1c3f746326..af8620269370 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -60,6 +60,7 @@ import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
@@ -108,6 +109,8 @@ import static com.android.server.wm.proto.DisplayProto.DPI;
import static com.android.server.wm.proto.DisplayProto.ID;
import static com.android.server.wm.proto.DisplayProto.IME_WINDOWS;
import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER;
+import static com.android.server.wm.proto.DisplayProto.ROTATION;
+import static com.android.server.wm.proto.DisplayProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.proto.DisplayProto.STACKS;
import android.annotation.NonNull;
@@ -2139,6 +2142,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
proto.write(DPI, mBaseDisplayDensity);
mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
+ proto.write(ROTATION, mRotation);
+ final ScreenRotationAnimation screenRotationAnimation =
+ mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ if (screenRotationAnimation != null) {
+ screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
+ }
proto.end(token);
}
@@ -3598,7 +3607,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
- if (policy.isKeyguardShowingAndNotOccluded()) {
+ if (policy.isKeyguardShowingAndNotOccluded()
+ || mService.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
return mLastKeyguardForcedOrientation;
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 08a9caa80d1e..d5b6d24631e6 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -25,12 +25,15 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import static com.android.server.wm.WindowSurfaceController.SurfaceTrace;
+import static com.android.server.wm.proto.ScreenRotationAnimationProto.ANIMATION_RUNNING;
+import static com.android.server.wm.proto.ScreenRotationAnimationProto.STARTED;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -198,7 +201,7 @@ class ScreenRotationAnimation {
pw.print(prefix); pw.print("mEnterTransformation=");
mEnterTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mFrameTransformation=");
- mEnterTransformation.printShortString(pw); pw.println();
+ mFrameTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mFrameInitialMatrix=");
mFrameInitialMatrix.printShortString(pw);
pw.println();
@@ -216,6 +219,13 @@ class ScreenRotationAnimation {
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(STARTED, mStarted);
+ proto.write(ANIMATION_RUNNING, mAnimRunning);
+ proto.end(token);
+ }
+
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
boolean isSecure, WindowManagerService service) {
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 5dc79f8500c0..5d1083e2be26 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -30,6 +30,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
/**
@@ -198,7 +199,7 @@ class WindowLayersController {
private void adjustSpecialWindows() {
// The following adjustments are beyond the highest docked-affected layer
- int layer = mHighestDockedAffectedLayer + WINDOW_LAYER_MULTIPLIER;
+ int layer = mHighestDockedAffectedLayer + TYPE_LAYER_OFFSET;
// Adjust the docked stack windows and dock divider above only the windows that are affected
// by the docked stack. When this happens, also boost the assistant window layers, otherwise
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
index 8242149d61be..613f01c20300 100644
--- a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
@@ -91,6 +91,7 @@ public class ManagedServicesTest extends NotificationTestCase {
private ArrayMap<Integer, String> mExpectedSecondaryPackages;
private ArrayMap<Integer, String> mExpectedSecondaryComponentNames;
+ // type : user : list of approved
private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary = new ArrayMap<>();
private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary = new ArrayMap<>();
@@ -578,6 +579,32 @@ public class ManagedServicesTest extends NotificationTestCase {
assertEquals(0, service.getAllowedComponents(10).size());
}
+ @Test
+ public void testOnUserRemoved() throws Exception {
+ for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, approvalLevel);
+ loadXml(service);
+
+ ArrayMap<Integer, String> verifyMap = mExpectedPrimary.get(service.mApprovalLevel);
+ String user0 = verifyMap.remove(0);
+ verifyMap = mExpectedSecondary.get(service.mApprovalLevel);
+ user0 = user0 + ":" + verifyMap.remove(0);
+
+ service.onUserRemoved(0);
+
+ for (String verifyValue : user0.split(":")) {
+ if (!TextUtils.isEmpty(verifyValue)) {
+ assertFalse("service type " + service.mApprovalLevel + ":" + verifyValue
+ + " is still allowed",
+ service.isPackageOrComponentAllowed(verifyValue, 0));
+ }
+ }
+
+ verifyExpectedApprovedEntries(service);
+ }
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -657,7 +684,8 @@ public class ManagedServicesTest extends NotificationTestCase {
for (String packageOrComponent : verifyMap.get(userId).split(":")) {
if (!TextUtils.isEmpty(packageOrComponent)) {
if (service.mApprovalLevel == APPROVAL_BY_PACKAGE) {
- assertTrue(packageOrComponent, service.isComponentEnabledForPackage(packageOrComponent));
+ assertTrue(packageOrComponent,
+ service.isComponentEnabledForPackage(packageOrComponent));
for (int i = 1; i <= 3; i ++) {
ComponentName componentName = ComponentName.unflattenFromString(
packageOrComponent +"/C" + i);
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index 1305a4cf0710..f1aad29a5c58 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -69,8 +69,7 @@ class NameMangler {
* The mangled name should contain symbols that are illegal to define in XML,
* so that there will never be name mangling collisions.
*/
- static std::string MangleEntry(const std::string& package,
- const std::string& name) {
+ static std::string MangleEntry(const std::string& package, const std::string& name) {
return package + "$" + name;
}
@@ -86,8 +85,8 @@ class NameMangler {
}
out_package->assign(out_name->data(), pivot);
- out_name->assign(out_name->data() + pivot + 1,
- out_name->size() - (pivot + 1));
+ std::string new_name = out_name->substr(pivot + 1);
+ *out_name = std::move(new_name);
return true;
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1c3ac2ad4f17..47549f01f8ca 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -605,7 +605,7 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
if (processed_item) {
// Fix up the reference.
if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
- TransformReferenceFromNamespace(parser, "", ref);
+ ResolvePackage(parser, ref);
}
return processed_item;
}
@@ -1074,15 +1074,13 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
return false;
}
- Maybe<Reference> maybe_key =
- ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_key) {
- diag_->Error(DiagMessage(source) << "invalid attribute name '"
- << maybe_name.value() << "'");
+ diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
return false;
}
- TransformReferenceFromNamespace(parser, "", &maybe_key.value());
+ ResolvePackage(parser, &maybe_key.value());
maybe_key.value().SetSource(source);
std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
@@ -1091,8 +1089,7 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
return false;
}
- style->entries.push_back(
- Style::Entry{std::move(maybe_key.value()), std::move(value)});
+ style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
return true;
}
@@ -1104,21 +1101,18 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
if (maybe_parent) {
- // If the parent is empty, we don't have a parent, but we also don't infer
- // either.
+ // If the parent is empty, we don't have a parent, but we also don't infer either.
if (!maybe_parent.value().empty()) {
std::string err_str;
- style->parent = ResourceUtils::ParseStyleParentReference(
- maybe_parent.value(), &err_str);
+ style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
if (!style->parent) {
diag_->Error(DiagMessage(out_resource->source) << err_str);
return false;
}
- // Transform the namespace prefix to the actual package name, and mark the
- // reference as
+ // Transform the namespace prefix to the actual package name, and mark the reference as
// private if appropriate.
- TransformReferenceFromNamespace(parser, "", &style->parent.value());
+ ResolvePackage(parser, &style->parent.value());
}
} else {
@@ -1127,8 +1121,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
size_t pos = style_name.find_last_of(u'.');
if (pos != std::string::npos) {
style->parent_inferred = true;
- style->parent = Reference(
- ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
+ style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
}
}
@@ -1373,7 +1366,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
}
Reference& child_ref = maybe_ref.value();
- xml::TransformReferenceFromNamespace(parser, "", &child_ref);
+ xml::ResolvePackage(parser, &child_ref);
// Create the ParsedResource that will add the attribute to the table.
ParsedResource child_resource;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index d4ff6188d48d..3a2faa9f0368 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -457,7 +457,8 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
const Source& src = doc->file.source;
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "linking " << src.path);
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "linking " << src.path << " (" << doc->file.name << ")");
}
XmlReferenceLinker xml_linker;
@@ -505,6 +506,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
for (auto& pkg : table->packages) {
+ CHECK(!pkg->name.empty()) << "Packages must have names when being linked";
+
for (auto& type : pkg->types) {
// Sort by config and name, so that we get better locality in the zip file.
config_sorted_files.clear();
@@ -701,7 +704,7 @@ class LinkCommand {
util::make_unique<AssetManagerSymbolSource>();
for (const std::string& path : options_.include_paths) {
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage(path) << "loading include path");
+ context_->GetDiagnostics()->Note(DiagMessage() << "including " << path);
}
// First try to load the file as a static lib.
@@ -819,11 +822,9 @@ class LinkCommand {
return app_info;
}
- /**
- * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
- * Postcondition: ResourceTable has only one package left. All others are
- * stripped, or there is an error and false is returned.
- */
+ // Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
+ // Postcondition: ResourceTable has only one package left. All others are
+ // stripped, or there is an error and false is returned.
bool VerifyNoExternalPackages() {
auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
return context_->GetCompilationPackage() != pkg->name || !pkg->id ||
@@ -965,7 +966,91 @@ class LinkCommand {
context_->GetDiagnostics()->Error(DiagMessage()
<< "failed writing to '" << out_path
<< "': " << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+ return true;
+ }
+
+ bool GenerateJavaClasses() {
+ // The set of packages whose R class to call in the main classes onResourcesLoaded callback.
+ std::vector<std::string> packages_to_callback;
+
+ JavaClassGeneratorOptions template_options;
+ template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ template_options.javadoc_annotations = options_.javadoc_annotations;
+
+ if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) {
+ template_options.use_final = false;
+ }
+
+ if (context_->GetPackageType() == PackageType::kSharedLib) {
+ template_options.use_final = false;
+ template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
+ }
+
+ const StringPiece actual_package = context_->GetCompilationPackage();
+ StringPiece output_package = context_->GetCompilationPackage();
+ if (options_.custom_java_package) {
+ // Override the output java package to the custom one.
+ output_package = options_.custom_java_package.value();
+ }
+
+ // Generate the private symbols if required.
+ if (options_.private_symbols) {
+ packages_to_callback.push_back(options_.private_symbols.value());
+
+ // If we defined a private symbols package, we only emit Public symbols
+ // to the original package, and private and public symbols to the private package.
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
+ options)) {
+ return false;
+ }
+ }
+
+ // Generate copies of the original package R class but with different package names.
+ // This is to support non-namespaced builds.
+ for (const std::string& extra_package : options_.extra_java_packages) {
+ packages_to_callback.push_back(extra_package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
+ return false;
+ }
+ }
+
+ // Generate R classes for each package that was merged (static library).
+ // Use the actual package's resources only.
+ for (const std::string& package : table_merger_->merged_packages()) {
+ packages_to_callback.push_back(package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, package, package, options)) {
+ return false;
+ }
}
+
+ // Generate the main public R class.
+ JavaClassGeneratorOptions options = template_options;
+
+ // Only generate public symbols if we have a private package.
+ if (options_.private_symbols) {
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ }
+
+ if (options.rewrite_callback_options) {
+ options.rewrite_callback_options.value().packages_to_callback =
+ std::move(packages_to_callback);
+ }
+
+ if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
+ options_.generate_text_symbols_path)) {
+ return false;
+ }
+
return true;
}
@@ -1097,15 +1182,17 @@ class LinkCommand {
bool result;
if (options_.no_static_lib_packages) {
- // Merge all resources as if they were in the compilation package. This is
- // the old behavior of aapt.
+ // Merge all resources as if they were in the compilation package. This is the old behavior
+ // of aapt.
- // Add the package to the set of --extra-packages so we emit an R.java for
- // each library package.
+ // Add the package to the set of --extra-packages so we emit an R.java for each library
+ // package.
if (!pkg->name.empty()) {
options_.extra_java_packages.insert(pkg->name);
}
+ // Clear the package name, so as to make the resources look like they are coming from the
+ // local package.
pkg->name = "";
if (override) {
result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
@@ -1673,8 +1760,7 @@ class LinkCommand {
bool error = false;
{
- // AndroidManifest.xml has no resource name, but the CallSite is built
- // from the name
+ // AndroidManifest.xml has no resource name, but the CallSite is built from the name
// (aka, which package the AndroidManifest.xml is coming from).
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
@@ -1727,72 +1813,7 @@ class LinkCommand {
}
if (options_.generate_java_class_path) {
- // The set of packages whose R class to call in the main classes
- // onResourcesLoaded callback.
- std::vector<std::string> packages_to_callback;
-
- JavaClassGeneratorOptions template_options;
- template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- template_options.javadoc_annotations = options_.javadoc_annotations;
-
- if (context_->GetPackageType() == PackageType::kStaticLib ||
- options_.generate_non_final_ids) {
- template_options.use_final = false;
- }
-
- if (context_->GetPackageType() == PackageType::kSharedLib) {
- template_options.use_final = false;
- template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
- }
-
- const StringPiece actual_package = context_->GetCompilationPackage();
- StringPiece output_package = context_->GetCompilationPackage();
- if (options_.custom_java_package) {
- // Override the output java package to the custom one.
- output_package = options_.custom_java_package.value();
- }
-
- // Generate the private symbols if required.
- if (options_.private_symbols) {
- packages_to_callback.push_back(options_.private_symbols.value());
-
- // If we defined a private symbols package, we only emit Public symbols
- // to the original package, and private and public symbols to the
- // private package.
- JavaClassGeneratorOptions options = template_options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
- options)) {
- return 1;
- }
- }
-
- // Generate all the symbols for all extra packages.
- for (const std::string& extra_package : options_.extra_java_packages) {
- packages_to_callback.push_back(extra_package);
-
- JavaClassGeneratorOptions options = template_options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
- return 1;
- }
- }
-
- // Generate the main public R class.
- JavaClassGeneratorOptions options = template_options;
-
- // Only generate public symbols if we have a private package.
- if (options_.private_symbols) {
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- }
-
- if (options.rewrite_callback_options) {
- options.rewrite_callback_options.value().packages_to_callback =
- std::move(packages_to_callback);
- }
-
- if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
- options_.generate_text_symbols_path)) {
+ if (!GenerateJavaClasses()) {
return 1;
}
}
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 857cdd5365a0..a17926067a0b 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -69,10 +69,10 @@ class Visitor : public xml::PackageAwareVisitor {
// Use an empty string for the compilation package because we don't want to default to
// the local package if the user specified name="style" or something. This should just
// be the default namespace.
- Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package, {});
+ Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
if (!maybe_pkg) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid namespace prefix '"
- << name.package << "'");
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "invalid namespace prefix '" << name.package << "'");
error_ = true;
return;
}
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Android.mk
new file mode 100644
index 000000000000..6361f9b8ae7d
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
new file mode 100644
index 000000000000..6ed07b0c5c73
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_PACKAGE_NAME := AaptTestNamespace_App
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ AaptTestNamespace_LibOne \
+ AaptTestNamespace_LibTwo
+LOCAL_AAPT_FLAGS := -v
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml
new file mode 100644
index 000000000000..6398a83ea1d2
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.aapt.namespace.app">
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
+
+ <application android:theme="@style/AppTheme" android:label="@string/app_name">
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml
new file mode 100644
index 000000000000..19bfd942a5bd
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:libone="http://schemas.android.com/apk/res/com.android.aapt.namespace.libone"
+ xmlns:libtwo="http://schemas.android.com/apk/res/com.android.aapt.namespace.libtwo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.android.aapt.namespace.libtwo.TextView
+ android:id="@+id/textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="@bool/always_true"
+ android:text="@libone:string/textview_text"
+ libtwo:textview_attr="?libone:theme_attr" />
+</RelativeLayout> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
new file mode 100644
index 000000000000..1b80d9542881
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="app_name">Namespace App</string>
+ <bool name="always_true">true</bool>
+
+ <style name="AppTheme" parent="com.android.aapt.namespace.libone:style/Theme">
+ <item name="android:colorPrimary">#3F51B5</item>
+ <item name="android:colorPrimaryDark">#303F9F</item>
+ <item name="android:colorAccent">#FF4081</item>
+ <item name="com.android.aapt.namespace.libone:theme_attr">
+ @com.android.aapt.namespace.libtwo:string/public_string
+ </item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java b/tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java
new file mode 100644
index 000000000000..fcb4c3c12f81
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.aapt.namespace.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Toast;
+
+public class MainActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ com.android.aapt.namespace.libtwo.TextView tv = findViewById(R.id.textview);
+
+
+
+ Toast.makeText(this, tv.getTextViewAttr(), Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
new file mode 100644
index 000000000000..b1cac68dae7a
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_MODULE := AaptTestNamespace_LibOne
+LOCAL_MODULE_TAGS := tests
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# We need this to retain the R.java generated for this library.
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml
new file mode 100644
index 000000000000..70b4b226624b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.aapt.namespace.libone">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml
new file mode 100644
index 000000000000..d2dcea0c0b1e
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <public type="string" name="textview_text" />
+ <string name="textview_text">LibOne\'s textview_text string!</string>
+
+ <public type="attr" name="theme_attr" />
+ <attr name="theme_attr" format="string" />
+
+ <public type="style" name="Theme" />
+ <style name="Theme" parent="android:Theme.Material.Light.DarkActionBar">
+ <item name="theme_attr">[Please override with your own value]</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
new file mode 100644
index 000000000000..dc16d1bbb420
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_MODULE := AaptTestNamespace_LibTwo
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# We need this to retain the R.java generated for this library.
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml
new file mode 100644
index 000000000000..32944a9b8f0b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.aapt.namespace.libtwo">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml
new file mode 100644
index 000000000000..0c5f5d8d55e0
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <public type="string" name="public_string" />
+ <string name="public_string">LibTwo\'s public string!</string>
+
+ <public type="attr" name="textview_attr" />
+ <attr name="textview_attr" format="string" />
+
+ <declare-styleable name="TextView">
+ <attr name="textview_attr" />
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java
new file mode 100644
index 000000000000..0f8024e58797
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java
@@ -0,0 +1,53 @@
+/*
+ * 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.aapt.namespace.libtwo;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+public class TextView extends android.widget.TextView {
+
+ private String mTextViewAttr;
+
+ public TextView(Context context) {
+ this(context, null);
+ }
+
+ public TextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TextView,
+ 0, 0);
+ try {
+ mTextViewAttr = ta.getString(R.styleable.TextView_textview_attr);
+ } finally {
+ ta.recycle();
+ }
+ }
+
+ public String getTextViewAttr() {
+ return mTextViewAttr;
+ }
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/Android.mk
new file mode 100644
index 000000000000..6361f9b8ae7d
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTest/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk
index 38bd5b5e3275..4d0c01d565a5 100644
--- a/tools/aapt2/integration-tests/AppOne/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk
@@ -18,12 +18,12 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := AaptTestAppOne
+LOCAL_PACKAGE_NAME := AaptTestStaticLib_App
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2
LOCAL_STATIC_ANDROID_LIBRARIES := \
- AaptTestStaticLibOne \
- AaptTestStaticLibTwo
+ AaptTestStaticLib_LibOne \
+ AaptTestStaticLib_LibTwo
LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions
include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
index a5f202dd22fc..a5f202dd22fc 100644
--- a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
diff --git a/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets/subdir/subsubdir/test.txt
index 125194943ec8..125194943ec8 100644
--- a/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets/subdir/subsubdir/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/assets/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets/test.txt
index 88266de2b4d4..88266de2b4d4 100644
--- a/tools/aapt2/integration-tests/AppOne/assets/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/new.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/new.txt
index f4963a95503a..f4963a95503a 100644
--- a/tools/aapt2/integration-tests/AppOne/assets2/new.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/new.txt
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/test.txt
index 5d8b36c0d52d..5d8b36c0d52d 100644
--- a/tools/aapt2/integration-tests/AppOne/assets2/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/cheap_transparency.png
index 0522a9979db9..0522a9979db9 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/cheap_transparency.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/complex.9.png
index baf9fff13ab5..baf9fff13ab5 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/complex.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png
index 4bff9b900643..4bff9b900643 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/image.xml
index 6132a75d85d0..6132a75d85d0 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/image.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/outline_8x8.9.png
index 7b331e16fcbd..7b331e16fcbd 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/outline_8x8.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_off_center_outline_32x16.9.png
index 0ec6c70a2b9f..0ec6c70a2b9f 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_off_center_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_outline_32x16.9.png
index e05708a089a3..e05708a089a3 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/test.9.png
index 33daa117ea9d..33daa117ea9d 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/test.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_3x3.9.png
index a11377a0d670..a11377a0d670 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_optical_bounds_3x3.9.png
index 6803e4243484..6803e4243484 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_3x3.9.png
index 1a3731bbc8b8..1a3731bbc8b8 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_optical_bounds_3x3.9.png
index 489ace292e1f..489ace292e1f 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/font/myfont-italic.ttf b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-italic.ttf
index e69de29bb2d1..e69de29bb2d1 100644
--- a/tools/aapt2/integration-tests/AppOne/res/font/myfont-italic.ttf
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-italic.ttf
diff --git a/tools/aapt2/integration-tests/AppOne/res/font/myfont-normal.ttf b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-normal.ttf
index e69de29bb2d1..e69de29bb2d1 100644
--- a/tools/aapt2/integration-tests/AppOne/res/font/myfont-normal.ttf
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-normal.ttf
diff --git a/tools/aapt2/integration-tests/AppOne/res/font/myfont.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont.xml
index 1fb67914894e..1fb67914894e 100644
--- a/tools/aapt2/integration-tests/AppOne/res/font/myfont.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout-v21/main.xml
index 9f5a4a85cbcf..724bfe4a9536 100644
--- a/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout-v21/main.xml
@@ -15,7 +15,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout/main.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
index ab1a251a7d56..aaa884bf7084 100644
--- a/tools/aapt2/integration-tests/AppOne/res/layout/main.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
@@ -15,7 +15,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout/special.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/special.xml
index 28c85ca92019..28c85ca92019 100644
--- a/tools/aapt2/integration-tests/AppOne/res/layout/special.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/special.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/navigation/home.xml
index ade271d60ab6..ade271d60ab6 100644
--- a/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/navigation/home.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/raw/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/res/raw/test.txt
index b14df6442ea5..b14df6442ea5 100644
--- a/tools/aapt2/integration-tests/AppOne/res/raw/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/raw/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/transition/transition_set.xml
index e10e6c2f53da..e10e6c2f53da 100644
--- a/tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/transition/transition_set.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values-v4/styles.xml
index d8c11e210eda..d8c11e210eda 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values-v4/styles.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/colors.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/colors.xml
index 4df5077051d2..4df5077051d2 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/colors.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/colors.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/styles.xml
index 19d96c0809db..a088e5d0e1a2 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/styles.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
+<resources>
<style name="App">
<item name="android:background">@color/primary</item>
<item name="android:colorPrimary">@color/primary</item>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/test.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/test.xml
index 2c9e8b877565..2c9e8b877565 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/test.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/test.xml
diff --git a/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java b/tools/aapt2/integration-tests/StaticLibTest/App/src/com/android/aapt/app/one/AppOne.java
index 472b35a781fe..472b35a781fe 100644
--- a/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/src/com/android/aapt/app/one/AppOne.java
diff --git a/tools/aapt2/integration-tests/StaticLibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk
index 0b7129aa0d38..0c828b80b3b3 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk
@@ -18,11 +18,11 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestStaticLibOne
+LOCAL_MODULE := AaptTestStaticLib_LibOne
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-# We need this to compile the Java sources of AaptTestStaticLibTwo using javac.
+# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
LOCAL_JAR_EXCLUDE_FILES := none
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/LibOne/AndroidManifest.xml
index 705047e71300..705047e71300 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/AndroidManifest.xml
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/layout/layout.xml
index 683c91cd9cf5..683c91cd9cf5 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/layout/layout.xml
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/values/values.xml
index b4dc90b3e640..b4dc90b3e640 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/values/values.xml
diff --git a/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/StaticLibTest/LibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
index cf48f67056cf..cf48f67056cf 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk
index 8b6eb41b08cd..538d5251b203 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk
@@ -18,10 +18,10 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestStaticLibTwo
+LOCAL_MODULE := AaptTestStaticLib_LibTwo
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
+LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLib_LibOne
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/AndroidManifest.xml
index 28f069932452..28f069932452 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/AndroidManifest.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/drawable/vector.xml
index dd5979f7e838..dd5979f7e838 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/drawable/vector.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/layout/layout_two.xml
index ba9830708eb0..ba9830708eb0 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/layout/layout_two.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/values/values.xml
index 97bb2a53d9f6..97bb2a53d9f6 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/values/values.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
index 7110dcdd017a..7110dcdd017a 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 44fa0f19a0e5..8da9106aa8d7 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -480,7 +480,7 @@ Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& packa
if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
// The entry name was mangled, and we successfully unmangled it.
// Check that we want to emit this symbol.
- if (package_name != unmangled_package) {
+ if (package_name_to_generate != unmangled_package) {
// Skip the entry if it doesn't belong to the package we're writing.
return {};
}
@@ -579,8 +579,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
continue;
}
- // Stay consistent with AAPT and generate an empty type class if the R class
- // is public.
+ // Stay consistent with AAPT and generate an empty type class if the R class is public.
const bool force_creation_if_empty =
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 5527f9092c87..3c9c4767b3d1 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -21,6 +21,7 @@
#include <unordered_set>
#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
#include "Resource.h"
#include "SdkConstants.h"
@@ -33,18 +34,15 @@ class ResourceTable;
class ResourceEntry;
struct ConfigDescription;
-/**
- * Defines the location in which a value exists. This determines visibility of
- * other package's private symbols.
- */
+// Defines the context in which a resource value is defined. Most resources are defined with the
+// implicit package name of their compilation context. Understanding the package name of a resource
+// allows to determine visibility of other symbols which may or may not have their packages defined.
struct CallSite {
- ResourceNameRef resource;
+ std::string package;
};
-/**
- * Determines whether a versioned resource should be created. If a versioned
- * resource already exists, it takes precedence.
- */
+// Determines whether a versioned resource should be created. If a versioned resource already
+// exists, it takes precedence.
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
const ApiVersion sdk_version_to_generate);
@@ -62,39 +60,26 @@ class AutoVersioner : public IResourceTableConsumer {
DISALLOW_COPY_AND_ASSIGN(AutoVersioner);
};
-/**
- * If any attribute resource values are defined as public, this consumer will
- * move all private
- * attribute resource values to a private ^private-attr type, avoiding backwards
- * compatibility
- * issues with new apps running on old platforms.
- *
- * The Android platform ignores resource attributes it doesn't recognize, so an
- * app developer can
- * use new attributes in their layout XML files without worrying about
- * versioning. This assumption
- * actually breaks on older platforms. OEMs may add private attributes that are
- * used internally.
- * AAPT originally assigned all private attributes IDs immediately proceeding
- * the public attributes'
- * IDs.
- *
- * This means that on a newer Android platform, an ID previously assigned to a
- * private attribute
- * may end up assigned to a public attribute.
- *
- * App developers assume using the newer attribute is safe on older platforms
- * because it will
- * be ignored. Instead, the platform thinks the new attribute is an older,
- * private attribute and
- * will interpret it as such. This leads to unintended styling and exceptions
- * thrown due to
- * unexpected types.
- *
- * By moving the private attributes to a completely different type, this ID
- * conflict will never
- * occur.
- */
+// If any attribute resource values are defined as public, this consumer will move all private
+// attribute resource values to a private ^private-attr type, avoiding backwards compatibility
+// issues with new apps running on old platforms.
+//
+// The Android platform ignores resource attributes it doesn't recognize, so an app developer can
+// use new attributes in their layout XML files without worrying about versioning. This assumption
+// actually breaks on older platforms. OEMs may add private attributes that are used internally.
+// AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
+// IDs.
+//
+// This means that on a newer Android platform, an ID previously assigned to a private attribute
+// may end up assigned to a public attribute.
+//
+// App developers assume using the newer attribute is safe on older platforms because it will
+// be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
+// will interpret it as such. This leads to unintended styling and exceptions thrown due to
+// unexpected types.
+//
+// By moving the private attributes to a completely different type, this ID conflict will never
+// occur.
class PrivateAttributeMover : public IResourceTableConsumer {
public:
PrivateAttributeMover() = default;
@@ -126,14 +111,10 @@ class ProductFilter : public IResourceTableConsumer {
std::unordered_set<std::string> products_;
};
-/**
- * Removes namespace nodes and URI information from the XmlResource.
- *
- * Once an XmlResource is processed by this consumer, it is no longer able to
- * have its attributes
- * parsed. As such, this XmlResource must have already been processed by
- * XmlReferenceLinker.
- */
+// Removes namespace nodes and URI information from the XmlResource.
+//
+// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
+// parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
class XmlNamespaceRemover : public IXmlResourceConsumer {
public:
explicit XmlNamespaceRemover(bool keep_uris = false) : keep_uris_(keep_uris){};
@@ -146,11 +127,8 @@ class XmlNamespaceRemover : public IXmlResourceConsumer {
bool keep_uris_;
};
-/**
- * Resolves attributes in the XmlResource and compiles string values to resource
- * values.
- * Once an XmlResource is processed by this linker, it is ready to be flattened.
- */
+// Resolves attributes in the XmlResource and compiles string values to resource values.
+// Once an XmlResource is processed by this linker, it is ready to be flattened.
class XmlReferenceLinker : public IXmlResourceConsumer {
public:
XmlReferenceLinker() = default;
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 414e56eb5dcc..71e828b039e1 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -30,23 +30,18 @@
#include "util/Util.h"
#include "xml/XmlUtil.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
namespace {
-/**
- * The ReferenceLinkerVisitor will follow all references and make sure they
- * point
- * to resources that actually exist, either in the local resource table, or as
- * external
- * symbols. Once the target resource has been found, the ID of the resource will
- * be assigned
- * to the reference object.
- *
- * NOTE: All of the entries in the ResourceTable must be assigned IDs.
- */
+// The ReferenceLinkerVisitor will follow all references and make sure they point
+// to resources that actually exist, either in the local resource table, or as external
+// symbols. Once the target resource has been found, the ID of the resource will be assigned
+// to the reference object.
+//
+// NOTE: All of the entries in the ResourceTable must be assigned IDs.
class ReferenceLinkerVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
@@ -65,14 +60,9 @@ class ReferenceLinkerVisitor : public ValueVisitor {
}
}
- /**
- * We visit the Style specially because during this phase, values of
- * attributes are
- * all RawString values. Now that we are expected to resolve all symbols, we
- * can
- * lookup the attributes to find out which types are allowed for the
- * attributes' values.
- */
+ // We visit the Style specially because during this phase, values of attributes are
+ // all RawString values. Now that we are expected to resolve all symbols, we can
+ // lookup the attributes to find out which types are allowed for the attributes' values.
void Visit(Style* style) override {
if (style->parent) {
Visit(&style->parent.value());
@@ -81,28 +71,21 @@ class ReferenceLinkerVisitor : public ValueVisitor {
for (Style::Entry& entry : style->entries) {
std::string err_str;
- // Transform the attribute reference so that it is using the fully
- // qualified package
- // name. This will also mark the reference as being able to see private
- // resources if
- // there was a '*' in the reference or if the package came from the
- // private namespace.
+ // Transform the attribute reference so that it is using the fully qualified package
+ // name. This will also mark the reference as being able to see private resources if
+ // there was a '*' in the reference or if the package came from the private namespace.
Reference transformed_reference = entry.key;
- TransformReferenceFromNamespace(package_decls_,
- context_->GetCompilationPackage(),
- &transformed_reference);
+ ResolvePackage(package_decls_, &transformed_reference);
- // Find the attribute in the symbol table and check if it is visible from
- // this callsite.
+ // Find the attribute in the symbol table and check if it is visible from this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
transformed_reference, callsite_, symbols_, &err_str);
if (symbol) {
- // Assign our style key the correct ID.
- // The ID may not exist.
+ // Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
- // Try to convert the value to a more specific, typed value based on the
- // attribute it is set to.
+ // Try to convert the value to a more specific, typed value based on the attribute it is
+ // set to.
entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
// Link/resolve the final value (mostly if it's a reference).
@@ -115,8 +98,8 @@ class ReferenceLinkerVisitor : public ValueVisitor {
// The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
- // Call the matches method again, this time with a DiagMessage so we
- // fill in the actual error message.
+ // Call the matches method again, this time with a DiagMessage so we fill in the actual
+ // error message.
symbol->attribute->Matches(*entry.value, &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -125,7 +108,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
} else {
DiagMessage msg(entry.key.GetSource());
msg << "style attribute '";
- ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
+ ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
msg << "' " << err_str;
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -133,17 +116,15 @@ class ReferenceLinkerVisitor : public ValueVisitor {
}
}
- bool HasError() { return error_; }
+ bool HasError() {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
- /**
- * Transform a RawString value into a more specific, appropriate value, based
- * on the
- * Attribute. If a non RawString value is passed in, this is an identity
- * transform.
- */
+ // Transform a RawString value into a more specific, appropriate value, based on the
+ // Attribute. If a non RawString value is passed in, this is an identity transform.
std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
const Attribute* attr) {
if (RawString* raw_string = ValueCast<RawString>(value.get())) {
@@ -178,11 +159,9 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
public:
EmptyDeclStack() = default;
- Maybe<xml::ExtractedPackage> TransformPackageAlias(
- const StringPiece& alias,
- const StringPiece& local_package) const override {
+ Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
if (alias.empty()) {
- return xml::ExtractedPackage{local_package.to_string(), true /* private */};
+ return xml::ExtractedPackage{{}, true /*private*/};
}
return {};
}
@@ -191,32 +170,44 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
};
-} // namespace
+// The symbol is visible if it is public, or if the reference to it is requesting private access
+// or if the callsite comes from the same package.
+bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
+ const CallSite& callsite) {
+ if (symbol.is_public || ref.private_reference) {
+ return true;
+ }
-/**
- * The symbol is visible if it is public, or if the reference to it is
- * requesting private access
- * or if the callsite comes from the same package.
- */
-bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
- const Reference& ref,
- const CallSite& callsite) {
- if (!symbol.is_public && !ref.private_reference) {
- if (ref.name) {
- return callsite.resource.package == ref.name.value().package;
- } else if (ref.id && symbol.id) {
- return ref.id.value().package_id() == symbol.id.value().package_id();
- } else {
- return false;
+ if (ref.name) {
+ const ResourceName& name = ref.name.value();
+ if (name.package.empty()) {
+ // If the symbol was found, and the package is empty, that means it was found in the local
+ // scope, which is always visible (private local).
+ return true;
}
+
+ // The symbol is visible if the reference is local to the same package it is defined in.
+ return callsite.package == name.package;
}
- return true;
+
+ if (ref.id && symbol.id) {
+ return ref.id.value().package_id() == symbol.id.value().package_id();
+ }
+ return false;
}
+} // namespace
+
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
+ const CallSite& callsite,
SymbolTable* symbols) {
if (reference.name) {
- return symbols->FindByName(reference.name.value());
+ const ResourceName& name = reference.name.value();
+ if (name.package.empty()) {
+ // Use the callsite's package name if no package name was defined.
+ return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
+ }
+ return symbols->FindByName(name);
} else if (reference.id) {
return symbols->FindById(reference.id.value());
} else {
@@ -228,7 +219,7 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
+ const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -274,24 +265,62 @@ Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference&
return xml::AaptAttribute(*symbol->attribute, symbol->id);
}
-void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
- const Reference& orig,
- const Reference& transformed) {
+void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
CHECK(out_msg != nullptr);
+ if (!ref.name) {
+ *out_msg << ref.id.value();
+ return;
+ }
- if (orig.name) {
- *out_msg << orig.name.value();
- if (transformed.name.value() != orig.name.value()) {
- *out_msg << " (aka " << transformed.name.value() << ")";
- }
- } else {
- *out_msg << orig.id.value();
+ *out_msg << ref.name.value();
+
+ Reference fully_qualified = ref;
+ xml::ResolvePackage(decls, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = callsite.package;
+ }
+
+ if (full_name != ref.name.value()) {
+ *out_msg << " (aka " << full_name << ")";
+ }
+}
+
+void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls,
+ DiagMessage* out_msg) {
+ CHECK(out_msg != nullptr);
+ if (!ref.name) {
+ *out_msg << ref.id.value();
+ return;
+ }
+
+ const ResourceName& ref_name = ref.name.value();
+ CHECK_EQ(ref_name.type, ResourceType::kAttr);
+
+ if (!ref_name.package.empty()) {
+ *out_msg << ref_name.package << ":";
+ }
+ *out_msg << ref_name.entry;
+
+ Reference fully_qualified = ref;
+ xml::ResolvePackage(decls, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = callsite.package;
+ }
+
+ if (full_name != ref.name.value()) {
+ *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
}
}
bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls) {
+ const xml::IPackageDeclStack* decls) {
CHECK(reference != nullptr);
if (!reference->name && !reference->id) {
// This is @null.
@@ -299,7 +328,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
}
Reference transformed_reference = *reference;
- TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
+ xml::ResolvePackage(decls, &transformed_reference);
std::string err_str;
const SymbolTable::Symbol* s =
@@ -314,7 +343,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
DiagMessage error_msg(reference->GetSource());
error_msg << "resource ";
- WriteResourceName(&error_msg, *reference, transformed_reference);
+ WriteResourceName(*reference, callsite, decls, &error_msg);
error_msg << " " << err_str;
context->GetDiagnostics()->Error(error_msg);
return false;
@@ -324,21 +353,24 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
EmptyDeclStack decl_stack;
bool error = false;
for (auto& package : table->packages) {
+ // Since we're linking, each package must have a name.
+ CHECK(!package->name.empty()) << "all packages being linked must have a name";
+
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- // Symbol state information may be lost if there is no value for the
- // resource.
- if (entry->symbol_status.state != SymbolState::kUndefined &&
- entry->values.empty()) {
- context->GetDiagnostics()->Error(
- DiagMessage(entry->symbol_status.source)
- << "no definition for declared symbol '"
- << ResourceNameRef(package->name, type->type, entry->name)
- << "'");
+ // First, unmangle the name if necessary.
+ ResourceName name(package->name, type->type, entry->name);
+ NameMangler::Unmangle(&name.entry, &name.package);
+
+ // Symbol state information may be lost if there is no value for the resource.
+ if (entry->symbol_status.state != SymbolState::kUndefined && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->symbol_status.source)
+ << "no definition for declared symbol '" << name << "'");
error = true;
}
- CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
+ // The context of this resource is the package in which it is defined.
+ const CallSite callsite{name.package};
ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
&table->string_pool, &decl_stack);
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index b3d0196d9e7c..3b11bee0acc9 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -29,83 +29,58 @@
namespace aapt {
-/**
- * Resolves all references to resources in the ResourceTable and assigns them
- * IDs.
- * The ResourceTable must already have IDs assigned to each resource.
- * Once the ResourceTable is processed by this linker, it is ready to be
- * flattened.
- */
+// Resolves all references to resources in the ResourceTable and assigns them IDs.
+// The ResourceTable must already have IDs assigned to each resource.
+// Once the ResourceTable is processed by this linker, it is ready to be flattened.
class ReferenceLinker : public IResourceTableConsumer {
public:
ReferenceLinker() = default;
- /**
- * Returns true if the symbol is visible by the reference and from the
- * callsite.
- */
- static bool IsSymbolVisible(const SymbolTable::Symbol& symbol,
- const Reference& ref, const CallSite& callsite);
-
- /**
- * Performs name mangling and looks up the resource in the symbol table.
- * Returns nullptr if the symbol was not found.
- */
- static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference, SymbolTable* symbols);
-
- /**
- * Performs name mangling and looks up the resource in the symbol table. If
- * the symbol is not visible by the reference at the callsite, nullptr is
- * returned. out_error holds the error message.
- */
+ // Performs name mangling and looks up the resource in the symbol table. Uses the callsite's
+ // package if the reference has no package name defined (implicit).
+ // Returns nullptr if the symbol was not found.
+ static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
+ const CallSite& callsite, SymbolTable* symbols);
+
+ // Performs name mangling and looks up the resource in the symbol table. If the symbol is not
+ // visible by the reference at the callsite, nullptr is returned.
+ // `out_error` holds the error message.
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
- /**
- * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is
- * an attribute.
- * That is, the return value will have a non-null value for
- * ISymbolTable::Symbol::attribute.
- */
+ // Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
+ // That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
- /**
- * Resolves the attribute reference and returns an xml::AaptAttribute if
- * successful.
- * If resolution fails, outError holds the error message.
- */
+ // Resolves the attribute reference and returns an xml::AaptAttribute if successful.
+ // If resolution fails, outError holds the error message.
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
- /**
- * Writes the resource name to the DiagMessage, using the
- * "orig_name (aka <transformed_name>)" syntax.
- */
- static void WriteResourceName(DiagMessage* out_msg, const Reference& orig,
- const Reference& transformed);
-
- /**
- * Transforms the package name of the reference to the fully qualified package
- * name using
- * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the
- * symbol is visible
- * to the reference at the callsite, the reference is updated with an ID.
- * Returns false on failure, and an error message is logged to the
- * IDiagnostics in the context.
- */
+ // Writes the resource name to the DiagMessage, using the
+ // "orig_name (aka <transformed_name>)" syntax.
+ static void WriteResourceName(const Reference& orig, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+
+ // Same as WriteResourceName but omits the 'attr' part.
+ static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+
+ // Transforms the package name of the reference to the fully qualified package name using
+ // the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
+ // to the reference at the callsite, the reference is updated with an ID.
+ // Returns false on failure, and an error message is logged to the IDiagnostics in the context.
static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
- SymbolTable* symbols, xml::IPackageDeclStack* decls);
+ SymbolTable* symbols, const xml::IPackageDeclStack* decls);
- /**
- * Links all references in the ResourceTable.
- */
+ // Links all references in the ResourceTable.
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 72a91689e392..be38b967c986 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -18,7 +18,9 @@
#include "test/Test.h"
-using android::ResTable_map;
+using ::android::ResTable_map;
+using ::testing::Eq;
+using ::testing::IsNull;
using ::testing::NotNull;
namespace aapt {
@@ -263,7 +265,7 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)
.Build());
std::string error;
- const CallSite call_site{ResourceNameRef("com.app.test", ResourceType::kString, "foo")};
+ const CallSite call_site{"com.app.test"};
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
*test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
ASSERT_THAT(symbol, NotNull());
@@ -281,7 +283,7 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
.Build());
std::string error;
- const CallSite call_site{ResourceNameRef("com.app.ext", ResourceType::kLayout, "foo")};
+ const CallSite call_site{"com.app.ext"};
EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
*test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
@@ -293,4 +295,27 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
EXPECT_TRUE(error.empty());
}
+TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
+ NameMangler mangler(NameManglerPolicy{"com.app.test"});
+ SymbolTable table(&mangler);
+ table.AppendSource(test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
+ .AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
+ .Build());
+
+ const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
+ CallSite{"com.app.test"}, &table);
+ ASSERT_THAT(s, NotNull());
+ EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
+
+ s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
+ &table);
+ ASSERT_THAT(s, NotNull());
+ EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
+
+ EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
+ CallSite{"com.app.bad"}, &table),
+ IsNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 10e837c725e5..93c904f1a743 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -24,7 +24,7 @@
#include "ValueVisitor.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -32,27 +32,23 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
const TableMergerOptions& options)
: context_(context), master_table_(out_table), options_(options) {
// Create the desired package that all tables will be merged into.
- master_package_ = master_table_->CreatePackage(
- context_->GetCompilationPackage(), context_->GetPackageId());
+ master_package_ =
+ master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
CHECK(master_package_ != nullptr) << "package name or ID already taken";
}
-bool TableMerger::Merge(const Source& src, ResourceTable* table,
- io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
+bool TableMerger::Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection) {
+ return MergeImpl(src, table, collection, false /*overlay*/, true /*allow_new*/);
}
bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay);
+ return MergeImpl(src, table, collection, true /*overlay*/, options_.auto_add_overlay);
}
-/**
- * This will merge packages with the same package name (or no package name).
- */
+// This will merge packages with the same package name (or no package name).
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
- io::IFileCollection* collection, bool overlay,
- bool allow_new) {
+ io::IFileCollection* collection, bool overlay, bool allow_new) {
bool error = false;
for (auto& package : table->packages) {
// Only merge an empty package or the package we're building.
@@ -62,9 +58,8 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
FileMergeCallback callback;
if (collection) {
- callback = [&](const ResourceNameRef& name,
- const ConfigDescription& config, FileReference* new_file,
- FileReference* old_file) -> bool {
+ callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* new_file, FileReference* old_file) -> bool {
// The old file's path points inside the APK, so we can use it as is.
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
@@ -78,45 +73,38 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
};
}
- // Merge here. Once the entries are merged and mangled, any references to
- // them are still valid. This is because un-mangled references are
- // mangled, then looked up at resolution time.
- // Also, when linking, we convert references with no package name to use
- // the compilation package name.
- error |= !DoMerge(src, table, package.get(), false /* mangle */, overlay,
- allow_new, callback);
+ // Merge here. Once the entries are merged and mangled, any references to them are still
+ // valid. This is because un-mangled references are mangled, then looked up at resolution
+ // time. Also, when linking, we convert references with no package name to use the compilation
+ // package name.
+ error |=
+ !DoMerge(src, table, package.get(), false /* mangle */, overlay, allow_new, callback);
}
}
return !error;
}
-/**
- * This will merge and mangle resources from a static library.
- */
-bool TableMerger::MergeAndMangle(const Source& src,
- const StringPiece& package_name,
- ResourceTable* table,
- io::IFileCollection* collection) {
+// This will merge and mangle resources from a static library.
+bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name,
+ ResourceTable* table, io::IFileCollection* collection) {
bool error = false;
for (auto& package : table->packages) {
// Warn of packages with an unrelated ID.
if (package_name != package->name) {
- context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
- << package->name);
+ context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " << package->name);
continue;
}
bool mangle = package_name != context_->GetCompilationPackage();
merged_packages_.insert(package->name);
- auto callback = [&](
- const ResourceNameRef& name, const ConfigDescription& config,
- FileReference* new_file, FileReference* old_file) -> bool {
+ auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* new_file, FileReference* old_file) -> bool {
// The old file's path points inside the APK, so we can use it as is.
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
- context_->GetDiagnostics()->Error(
- DiagMessage(src) << "file '" << *old_file->path << "' not found");
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "file '" << *old_file->path << "' not found");
return false;
}
@@ -124,21 +112,18 @@ bool TableMerger::MergeAndMangle(const Source& src,
return true;
};
- error |= !DoMerge(src, table, package.get(), mangle, false /* overlay */,
- true /* allow new */, callback);
+ error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/,
+ callback);
}
return !error;
}
-static bool MergeType(IAaptContext* context, const Source& src,
- ResourceTableType* dst_type,
+static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type,
ResourceTableType* src_type) {
if (dst_type->symbol_status.state < src_type->symbol_status.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
+ // The incoming type's visibility is stronger, so we should override the visibility.
if (src_type->symbol_status.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is
- // meaningless.
+ // Only copy the ID if the source is public, or else the ID is meaningless.
dst_type->id = src_type->id;
}
dst_type->symbol_status = std::move(src_type->symbol_status);
@@ -155,14 +140,12 @@ static bool MergeType(IAaptContext* context, const Source& src,
return true;
}
-static bool MergeEntry(IAaptContext* context, const Source& src,
- ResourceEntry* dst_entry, ResourceEntry* src_entry) {
+static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry,
+ ResourceEntry* src_entry) {
if (dst_entry->symbol_status.state < src_entry->symbol_status.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
+ // The incoming type's visibility is stronger, so we should override the visibility.
if (src_entry->symbol_status.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is
- // meaningless.
+ // Only copy the ID if the source is public, or else the ID is meaningless.
dst_entry->id = src_entry->id;
}
dst_entry->symbol_status = std::move(src_entry->symbol_status);
@@ -171,9 +154,8 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
dst_entry->id && src_entry->id &&
dst_entry->id.value() != src_entry->id.value()) {
// Both entries are public and have different IDs.
- context->GetDiagnostics()->Error(
- DiagMessage(src) << "cannot merge entry '" << src_entry->name
- << "': conflicting public IDs");
+ context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name
+ << "': conflicting public IDs");
return false;
}
return true;
@@ -181,12 +163,10 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
// Modified CollisionResolver which will merge Styleables and Styles. Used with overlays.
//
-// Styleables are not actual resources, but they are treated as such during the
-// compilation phase.
+// Styleables are not actual resources, but they are treated as such during the compilation phase.
//
-// Styleables and Styles don't simply overlay each other, their definitions merge
-// and accumulate. If both values are Styleables/Styles, we just merge them into the
-// existing value.
+// Styleables and Styles don't simply overlay each other, their definitions merge and accumulate.
+// If both values are Styleables/Styles, we just merge them into the existing value.
static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming,
StringPool* pool) {
if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) {
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index c96b1b0b4dfb..81518ffb2441 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -33,81 +33,49 @@
namespace aapt {
struct TableMergerOptions {
- /**
- * If true, resources in overlays can be added without previously having
- * existed.
- */
+ // If true, resources in overlays can be added without previously having existed.
bool auto_add_overlay = false;
};
-/**
- * TableMerger takes resource tables and merges all packages within the tables
- * that have the same
- * package ID.
- *
- * If a package has a different name, all the entries in that table have their
- * names mangled
- * to include the package name. This way there are no collisions. In order to do
- * this correctly,
- * the TableMerger needs to also mangle any FileReference paths. Once these are
- * mangled,
- * the original source path of the file, along with the new destination path is
- * recorded in the
- * queue returned from getFileMergeQueue().
- *
- * Once the merging is complete, a separate process can go collect the files
- * from the various
- * source APKs and either copy or process their XML and put them in the correct
- * location in
- * the final APK.
- */
+// TableMerger takes resource tables and merges all packages within the tables that have the same
+// package ID.
+//
+// If a package has a different name, all the entries in that table have their names mangled
+// to include the package name. This way there are no collisions. In order to do this correctly,
+// the TableMerger needs to also mangle any FileReference paths. Once these are mangled, the
+// `IFile` pointer in `FileReference` will point to the original file.
+//
+// Once the merging is complete, a separate phase can go collect the files from the various
+// source APKs and either copy or process their XML and put them in the correct location in the
+// final APK.
class TableMerger {
public:
- /**
- * Note: The out_table ResourceTable must live longer than this TableMerger.
- * References are made to this ResourceTable for efficiency reasons.
- */
- TableMerger(IAaptContext* context, ResourceTable* out_table,
- const TableMergerOptions& options);
-
- const std::set<std::string>& merged_packages() const {
+ // Note: The out_table ResourceTable must live longer than this TableMerger.
+ // References are made to this ResourceTable for efficiency reasons.
+ TableMerger(IAaptContext* context, ResourceTable* out_table, const TableMergerOptions& options);
+
+ inline const std::set<std::string>& merged_packages() const {
return merged_packages_;
}
- /**
- * Merges resources from the same or empty package. This is for local sources.
- * An io::IFileCollection is optional and used to find the referenced Files
- * and process them.
- */
- bool Merge(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
-
- /**
- * Merges resources from an overlay ResourceTable.
- * An io::IFileCollection is optional and used to find the referenced Files
- * and process them.
- */
+ // Merges resources from the same or empty package. This is for local sources.
+ // An io::IFileCollection is optional and used to find the referenced Files and process them.
+ bool Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection = nullptr);
+
+ // Merges resources from an overlay ResourceTable.
+ // An io::IFileCollection is optional and used to find the referenced Files and process them.
bool MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection = nullptr);
- /**
- * Merges resources from the given package, mangling the name. This is for
- * static libraries.
- * An io::IFileCollection is needed in order to find the referenced Files and
- * process them.
- */
+ // Merges resources from the given package, mangling the name. This is for static libraries.
+ // An io::IFileCollection is needed in order to find the referenced Files and process them.
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
io::IFileCollection* collection);
- /**
- * Merges a compiled file that belongs to this same or empty package. This is
- * for local sources.
- */
+ // Merges a compiled file that belongs to this same or empty package. This is for local sources.
bool MergeFile(const ResourceFile& fileDesc, io::IFile* file);
- /**
- * Merges a compiled file from an overlay, overriding an existing definition.
- */
+ // Merges a compiled file from an overlay, overriding an existing definition.
bool MergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
private:
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index bcecd2003846..6ebb80f4be8f 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -31,13 +31,9 @@ namespace aapt {
namespace {
-/**
- * Visits all references (including parents of styles, references in styles,
- * arrays, etc) and
- * links their symbolic name to their Resource ID, performing mangling and
- * package aliasing
- * as needed.
- */
+// Visits all references (including parents of styles, references in styles, arrays, etc) and
+// links their symbolic name to their Resource ID, performing mangling and package aliasing
+// as needed.
class ReferenceVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
@@ -52,7 +48,9 @@ class ReferenceVisitor : public ValueVisitor {
}
}
- bool HasError() const { return error_; }
+ bool HasError() const {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
@@ -64,9 +62,7 @@ class ReferenceVisitor : public ValueVisitor {
bool error_;
};
-/**
- * Visits each xml Element and compiles the attributes within.
- */
+// Visits each xml Element and compiles the attributes within.
class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
@@ -92,18 +88,12 @@ class XmlVisitor : public xml::PackageAwareVisitor {
// they were assigned to the default Attribute.
const Attribute* attribute = &kDefaultAttribute;
- std::string attribute_package;
if (Maybe<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
// There is a valid package name for this attribute. We will look this up.
- attribute_package = maybe_package.value().package;
- if (attribute_package.empty()) {
- // Empty package means the 'current' or 'local' package.
- attribute_package = context_->GetCompilationPackage();
- }
-
- Reference attr_ref(ResourceNameRef(attribute_package, ResourceType::kAttr, attr.name));
+ Reference attr_ref(
+ ResourceNameRef(maybe_package.value().package, ResourceType::kAttr, attr.name));
attr_ref.private_reference = maybe_package.value().private_namespace;
std::string err_str;
@@ -111,9 +101,11 @@ class XmlVisitor : public xml::PackageAwareVisitor {
ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
if (!attr.compiled_attribute) {
- context_->GetDiagnostics()->Error(DiagMessage(source) << "attribute '"
- << attribute_package << ":"
- << attr.name << "' " << err_str);
+ DiagMessage error_msg(source);
+ error_msg << "attribute ";
+ ReferenceLinker::WriteAttributeName(attr_ref, callsite_, this, &error_msg);
+ error_msg << " " << err_str;
+ context_->GetDiagnostics()->Error(error_msg);
error_ = true;
continue;
}
@@ -129,12 +121,8 @@ class XmlVisitor : public xml::PackageAwareVisitor {
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
// We won't be able to encode this as a string.
DiagMessage msg(source);
- msg << "'" << attr.value << "' "
- << "is incompatible with attribute ";
- if (!attribute_package.empty()) {
- msg << attribute_package << ":";
- }
- msg << attr.name << " " << *attribute;
+ msg << "'" << attr.value << "' is incompatible with attribute " << attr.name << " "
+ << *attribute;
context_->GetDiagnostics()->Error(msg);
error_ = true;
}
@@ -163,7 +151,17 @@ class XmlVisitor : public xml::PackageAwareVisitor {
} // namespace
bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
- const CallSite callsite = {resource->file.name};
+ CallSite callsite{resource->file.name.package};
+
+ std::string out_name = resource->file.name.entry;
+ NameMangler::Unmangle(&out_name, &callsite.package);
+
+ if (callsite.package.empty()) {
+ // Assume an empty package means that the XML file is local. This is true of AndroidManifest.xml
+ // for example.
+ callsite.package = context->GetCompilationPackage();
+ }
+
XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
if (resource->root) {
resource->root->Accept(&visitor);
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index cbb652ed9a1a..19de3afb9d62 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -274,6 +274,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
switch (code) {
case ResXMLParser::START_NAMESPACE: {
NamespaceDecl decl;
+ decl.line_number = tree.getLineNumber();
+
size_t len;
const char16_t* str16 = tree.getNamespacePrefix(&len);
if (str16) {
@@ -288,6 +290,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
if (pending_element == nullptr) {
pending_element = util::make_unique<Element>();
}
+ pending_element->namespace_decls.push_back(std::move(decl));
break;
}
@@ -297,8 +300,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
el = std::move(pending_element);
} else {
el = util::make_unique<Element>();
- ;
}
+ el->line_number = tree.getLineNumber();
size_t len;
const char16_t* str16 = tree.getElementNamespace(&len);
@@ -479,10 +482,9 @@ void PackageAwareVisitor::AfterVisitElement(Element* el) {
package_decls_.pop_back();
}
-Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
- const StringPiece& alias, const StringPiece& local_package) const {
+Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
if (alias.empty()) {
- return ExtractedPackage{local_package.to_string(), false /* private */};
+ return ExtractedPackage{{}, false /*private*/};
}
const auto rend = package_decls_.rend();
@@ -493,7 +495,7 @@ Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
const PackageDecl& decl = *iter2;
if (alias == decl.prefix) {
if (decl.package.package.empty()) {
- return ExtractedPackage{local_package.to_string(), decl.package.private_namespace};
+ return ExtractedPackage{{}, decl.package.private_namespace};
}
return decl.package;
}
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 154224381626..9a9151da4ab7 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -185,8 +185,7 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
using Visitor::Visit;
- Maybe<ExtractedPackage> TransformPackageAlias(
- const android::StringPiece& alias, const android::StringPiece& local_package) const override;
+ Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
protected:
PackageAwareVisitor() = default;
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 6ed2d616f782..10a45870e556 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -86,19 +86,14 @@ class TestVisitor : public PackageAwareVisitor {
void Visit(Element* el) override {
if (el->name == "View1") {
- EXPECT_THAT(TransformPackageAlias("one", "local"),
- Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
} else if (el->name == "View2") {
- EXPECT_THAT(TransformPackageAlias("one", "local"),
- Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two", "local"),
- Eq(make_value(ExtractedPackage{"com.two", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
} else if (el->name == "View3") {
- EXPECT_THAT(TransformPackageAlias("one", "local"),
- Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two", "local"),
- Eq(make_value(ExtractedPackage{"com.two", false})));
- EXPECT_THAT(TransformPackageAlias("three", "local"),
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
+ EXPECT_THAT(TransformPackageAlias("three"),
Eq(make_value(ExtractedPackage{"com.three", false})));
}
}
@@ -112,7 +107,6 @@ TEST(XmlDomTest, PackageAwareXmlVisitor) {
</View2>
</View1>)");
- Debug::DumpXml(doc.get());
TestVisitor visitor;
doc->root->Accept(&visitor);
}
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 30bdc507303b..402e5a459f4e 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -141,17 +141,16 @@ const std::string& XmlPullParser::namespace_uri() const {
return event_queue_.front().data2;
}
-Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
- const StringPiece& alias, const StringPiece& local_package) const {
+Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
if (alias.empty()) {
- return ExtractedPackage{local_package.to_string(), false /* private */};
+ return ExtractedPackage{{}, false /*private*/};
}
const auto end_iter = package_aliases_.rend();
for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
if (alias == iter->prefix) {
if (iter->package.package.empty()) {
- return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
+ return ExtractedPackage{{}, iter->package.private_namespace};
}
return iter->package;
}
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index a00caa139061..63db66f0b2b7 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -119,8 +119,7 @@ class XmlPullParser : public IPackageDeclStack {
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
- Maybe<ExtractedPackage> TransformPackageAlias(
- const android::StringPiece& alias, const android::StringPiece& local_package) const override;
+ Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
//
// Remaining methods are for retrieving information about attributes
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index fb8cee8b5634..c1186e83369c 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -62,19 +62,15 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
return {};
}
-void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
- const StringPiece& local_package,
- Reference* in_ref) {
+void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
if (in_ref->name) {
if (Maybe<ExtractedPackage> transformed_package =
- decl_stack->TransformPackageAlias(in_ref->name.value().package,
- local_package)) {
+ decl_stack->TransformPackageAlias(in_ref->name.value().package)) {
ExtractedPackage& extracted_package = transformed_package.value();
in_ref->name.value().package = std::move(extracted_package.package);
// If the reference was already private (with a * prefix) and the
- // namespace is public,
- // we keep the reference private.
+ // namespace is public, we keep the reference private.
in_ref->private_reference |= extracted_package.private_namespace;
}
}
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 866b6dcd7a88..4eb359a9eed4 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -35,7 +35,7 @@ constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
// Result of extracting a package name from a namespace URI declaration.
struct ExtractedPackage {
// The name of the package. This can be the empty string, which means that the package
- // should be assumed to be the package being compiled.
+ // should be assumed to be the same as the CallSite it was defined in.
std::string package;
// True if the package's private namespace was declared. This means that private resources
@@ -51,8 +51,8 @@ struct ExtractedPackage {
// http://schemas.android.com/apk/res/<package> or
// http://schemas.android.com/apk/prv/res/<package>
//
-// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
-// returns an empty package name.
+// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, returns an empty
+// package name.
Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
// Returns an XML Android namespace for the given package of the form:
@@ -63,21 +63,20 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace
std::string BuildPackageNamespace(const android::StringPiece& package,
bool private_reference = false);
-// Interface representing a stack of XML namespace declarations. When looking up the package
-// for a namespace prefix, the stack is checked from top to bottom.
+// Interface representing a stack of XML namespace declarations. When looking up the package for a
+// namespace prefix, the stack is checked from top to bottom.
struct IPackageDeclStack {
virtual ~IPackageDeclStack() = default;
// Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
virtual Maybe<ExtractedPackage> TransformPackageAlias(
- const android::StringPiece& alias, const android::StringPiece& local_package) const = 0;
+ const android::StringPiece& alias) const = 0;
};
// Helper function for transforming the original Reference inRef to a fully qualified reference
// via the IPackageDeclStack. This will also mark the Reference as private if the namespace of the
// package declaration was private.
-void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
- const android::StringPiece& local_package, Reference* in_ref);
+void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref);
} // namespace xml
} // namespace aapt
diff --git a/tools/locked_region_code_injection/Android.mk b/tools/locked_region_code_injection/Android.mk
index d9217834f871..77d5163c1b78 100644
--- a/tools/locked_region_code_injection/Android.mk
+++ b/tools/locked_region_code_injection/Android.mk
@@ -10,6 +10,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
asm-commons-5.2 \
asm-tree-5.2 \
asm-analysis-5.2 \
- guava-20.0 \
+ guava-21.0 \
include $(BUILD_HOST_JAVA_LIBRARY)