diff options
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 Binary files differindex 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 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 Binary files differindex 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 diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png Binary files differindex 4bff9b900643..4bff9b900643 100644 --- a/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png +++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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) |