Merge "Don't use pow for distance calculation" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bd66c1c..720bfc0 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -358,7 +358,7 @@
visibility: ["//visibility:private"],
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":api-stubs-docs-non-updatable"],
@@ -368,7 +368,7 @@
},
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs.system",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":system-api-stubs-docs-non-updatable"],
@@ -378,7 +378,7 @@
},
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs.module_lib",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":module-lib-api-stubs-docs-non-updatable"],
@@ -392,7 +392,7 @@
},
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs.test",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":test-api-stubs-docs-non-updatable"],
@@ -415,7 +415,7 @@
defaults_visibility: ["//frameworks/base/services"],
}
-java_library_static {
+java_library {
name: "android_stubs_current",
static_libs: modules_public_stubs + [
"android-non-updatable.stubs",
@@ -424,7 +424,7 @@
defaults: ["android.jar_defaults"],
}
-java_library_static {
+java_library {
name: "android_system_stubs_current",
static_libs: modules_system_stubs + [
"android-non-updatable.stubs.system",
@@ -450,7 +450,7 @@
],
}
-java_library_static {
+java_library {
name: "android_test_stubs_current",
// Modules do not have test APIs, but we want to include their SystemApis, like we include
// the SystemApi of framework-non-updatable-sources.
@@ -467,7 +467,7 @@
},
}
-java_library_static {
+java_library {
name: "android_module_lib_stubs_current",
defaults: [
"android.jar_defaults",
@@ -531,7 +531,7 @@
visibility: ["//visibility:private"],
}
-java_library_static {
+java_library {
name: "hwbinder.stubs",
sdk_version: "core_current",
libs: ["framework-annotations-lib"],
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index aafeb48..54e47cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -43,6 +43,11 @@
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_EXACT_PERMISSION_REVOKED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_PI_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
import android.Manifest;
import android.annotation.NonNull;
@@ -116,6 +121,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
+import com.android.internal.util.RingBuffer;
import com.android.internal.util.StatLogger;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
@@ -160,6 +166,7 @@
public class AlarmManagerService extends SystemService {
private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
+ private static final int REMOVAL_HISTORY_SIZE_PER_UID = 10;
static final int TIME_CHANGED_MASK = 1 << 16;
static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK | ELAPSED_REALTIME_WAKEUP_MASK;
@@ -238,6 +245,7 @@
AppWakeupHistory mAppWakeupHistory;
AppWakeupHistory mAllowWhileIdleHistory;
private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
+ private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
IBinder.DeathRecipient mListenerDeathRecipient;
@@ -392,6 +400,57 @@
}
}
+ static class RemovedAlarm {
+ static final int REMOVE_REASON_UNDEFINED = 0;
+ static final int REMOVE_REASON_ALARM_CANCELLED = 1;
+ static final int REMOVE_REASON_EXACT_PERMISSION_REVOKED = 2;
+ static final int REMOVE_REASON_DATA_CLEARED = 3;
+ static final int REMOVE_REASON_PI_CANCELLED = 4;
+
+ final String mTag;
+ final long mWhenRemovedElapsed;
+ final long mWhenRemovedRtc;
+ final int mRemoveReason;
+
+ RemovedAlarm(Alarm a, int removeReason, long nowRtc, long nowElapsed) {
+ mTag = a.statsTag;
+ mRemoveReason = removeReason;
+ mWhenRemovedRtc = nowRtc;
+ mWhenRemovedElapsed = nowElapsed;
+ }
+
+ static final boolean isLoggable(int reason) {
+ // We don't want to log meaningless reasons. This also gives a way for callers to
+ // opt out of logging, e.g. when replacing an alarm.
+ return reason != REMOVE_REASON_UNDEFINED;
+ }
+
+ static final String removeReasonToString(int reason) {
+ switch (reason) {
+ case REMOVE_REASON_ALARM_CANCELLED:
+ return "alarm_cancelled";
+ case REMOVE_REASON_EXACT_PERMISSION_REVOKED:
+ return "exact_alarm_permission_revoked";
+ case REMOVE_REASON_DATA_CLEARED:
+ return "data_cleared";
+ case REMOVE_REASON_PI_CANCELLED:
+ return "pi_cancelled";
+ default:
+ return "unknown:" + reason;
+ }
+ }
+
+ void dump(IndentingPrintWriter pw, long nowElapsed, SimpleDateFormat sdf) {
+ pw.print("[tag", mTag);
+ pw.print("reason", removeReasonToString(mRemoveReason));
+ pw.print("elapsed=");
+ TimeUtils.formatDuration(mWhenRemovedElapsed, nowElapsed, pw);
+ pw.print(" rtc=");
+ pw.print(sdf.format(new Date(mWhenRemovedRtc)));
+ pw.println("]");
+ }
+ }
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -1691,7 +1750,7 @@
void removeImpl(PendingIntent operation, IAlarmListener listener) {
synchronized (mLock) {
- removeLocked(operation, listener);
+ removeLocked(operation, listener, REMOVE_REASON_UNDEFINED);
}
}
@@ -1812,7 +1871,7 @@
+ " -- package not allowed to start");
return;
}
- removeLocked(operation, directReceiver);
+ removeLocked(operation, directReceiver, REMOVE_REASON_UNDEFINED);
incrementAlarmCount(a.uid);
setImplLocked(a);
MetricsHelper.pushAlarmScheduled(a);
@@ -2106,7 +2165,7 @@
@Override
public void removeAlarmsForUid(int uid) {
synchronized (mLock) {
- removeLocked(uid);
+ removeLocked(uid, REMOVE_REASON_DATA_CLEARED);
}
}
@@ -2342,7 +2401,7 @@
return;
}
synchronized (mLock) {
- removeLocked(operation, listener);
+ removeLocked(operation, listener, REMOVE_REASON_ALARM_CANCELLED);
}
}
@@ -2688,6 +2747,7 @@
pw.println("Allow while idle history:");
mAllowWhileIdleHistory.dump(pw, nowELAPSED);
+ pw.println();
if (mLastPriorityAlarmDispatch.size() > 0) {
pw.println("Last priority alarm dispatches:");
@@ -2702,6 +2762,23 @@
pw.decreaseIndent();
}
+ if (mRemovalHistory.size() > 0) {
+ pw.println("Removal history: ");
+ pw.increaseIndent();
+ for (int i = 0; i < mRemovalHistory.size(); i++) {
+ UserHandle.formatUid(pw, mRemovalHistory.keyAt(i));
+ pw.println(":");
+ pw.increaseIndent();
+ final RemovedAlarm[] historyForUid = mRemovalHistory.valueAt(i).toArray();
+ for (final RemovedAlarm removedAlarm : historyForUid) {
+ removedAlarm.dump(pw, nowELAPSED, sdf);
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
+
if (mLog.dump(pw, "Recent problems:")) {
pw.println();
}
@@ -3239,7 +3316,7 @@
}
return !hasScheduleExactAlarmInternal(a.packageName, a.uid);
};
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
}
/**
@@ -3247,7 +3324,6 @@
* that the app is no longer eligible to use.
*
* This is not expected to get called frequently.
- * TODO (b/179541791): Add revocation history to dumpsys.
*/
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!");
@@ -3263,26 +3339,22 @@
}
return false;
};
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
}
- private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms) {
+ private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) {
+ final long nowRtc = mInjector.getCurrentTimeMillis();
+ final long nowElapsed = mInjector.getElapsedRealtime();
+
final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
- final boolean didRemove = !removedAlarms.isEmpty();
- if (didRemove) {
- for (final Alarm removed : removedAlarms) {
- decrementAlarmCount(removed.uid, 1);
- }
- }
+ final boolean removedFromStore = !removedAlarms.isEmpty();
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
final Alarm alarm = alarmsForUid.get(j);
if (whichAlarms.test(alarm)) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- alarmsForUid.remove(j);
- decrementAlarmCount(alarm.uid, 1);
+ removedAlarms.add(alarmsForUid.remove(j));
}
}
if (alarmsForUid.size() == 0) {
@@ -3292,13 +3364,24 @@
for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
final Alarm a = mPendingNonWakeupAlarms.get(i);
if (whichAlarms.test(a)) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingNonWakeupAlarms.remove(i);
- decrementAlarmCount(a.uid, 1);
+ removedAlarms.add(mPendingNonWakeupAlarms.remove(i));
}
}
- if (didRemove) {
+ for (final Alarm removed : removedAlarms) {
+ decrementAlarmCount(removed.uid, 1);
+ if (!RemovedAlarm.isLoggable(reason)) {
+ continue;
+ }
+ RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
+ if (bufferForUid == null) {
+ bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
+ mRemovalHistory.put(removed.uid, bufferForUid);
+ }
+ bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed));
+ }
+
+ if (removedFromStore) {
boolean idleUntilUpdated = false;
if (mPendingIdleUntil != null && whichAlarms.test(mPendingIdleUntil)) {
mPendingIdleUntil = null;
@@ -3320,7 +3403,7 @@
}
}
- void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+ void removeLocked(PendingIntent operation, IAlarmListener directReceiver, int reason) {
if (operation == null && directReceiver == null) {
if (localLOGV) {
Slog.w(TAG, "requested remove() of null operation",
@@ -3328,15 +3411,15 @@
}
return;
}
- removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver));
+ removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver), reason);
}
- void removeLocked(final int uid) {
+ void removeLocked(final int uid, int reason) {
if (uid == Process.SYSTEM_UID) {
// If a force-stop occurs for a system-uid package, ignore it.
return;
}
- removeAlarmsInternalLocked(a -> a.uid == uid);
+ removeAlarmsInternalLocked(a -> a.uid == uid, reason);
}
void removeLocked(final String packageName) {
@@ -3347,7 +3430,7 @@
}
return;
}
- removeAlarmsInternalLocked(a -> a.matches(packageName));
+ removeAlarmsInternalLocked(a -> a.matches(packageName), REMOVE_REASON_UNDEFINED);
}
// Only called for ephemeral apps
@@ -3358,7 +3441,7 @@
}
final Predicate<Alarm> whichAlarms = (a) -> (a.uid == uid
&& mActivityManagerInternal.isAppStartModeDisabled(uid, a.packageName));
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
}
void removeUserLocked(int userHandle) {
@@ -3368,13 +3451,18 @@
}
final Predicate<Alarm> whichAlarms =
(Alarm a) -> UserHandle.getUserId(a.uid) == userHandle;
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
mLastPriorityAlarmDispatch.removeAt(i);
}
}
+ for (int i = mRemovalHistory.size() - 1; i >= 0; i--) {
+ if (UserHandle.getUserId(mRemovalHistory.keyAt(i)) == userHandle) {
+ mRemovalHistory.removeAt(i);
+ }
+ }
}
void interactiveStateChangedLocked(boolean interactive) {
@@ -4018,7 +4106,7 @@
case REMOVE_FOR_CANCELED:
final PendingIntent operation = (PendingIntent) msg.obj;
synchronized (mLock) {
- removeLocked(operation, null);
+ removeLocked(operation, null, REMOVE_REASON_PI_CANCELLED);
}
break;
@@ -4198,6 +4286,7 @@
return;
case Intent.ACTION_UID_REMOVED:
mLastPriorityAlarmDispatch.delete(uid);
+ mRemovalHistory.delete(uid);
return;
case Intent.ACTION_PACKAGE_REMOVED:
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -4227,7 +4316,7 @@
// package-removed and package-restarted case
mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
- removeLocked(uid);
+ removeLocked(uid, REMOVE_REASON_UNDEFINED);
} else {
// external-applications-unavailable case
removeLocked(pkg);
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
index 3d5321c..e347ebf 100644
--- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
@@ -19,7 +19,7 @@
import android.media.MediaParceledListSlice;
/** {@hide} */
-interface IMediaCommunicationServiceCallback {
+oneway interface IMediaCommunicationServiceCallback {
void onSession2Created(in Session2Token token);
void onSession2Changed(in MediaParceledListSlice tokens);
}
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 06de3f8..ed31aa3 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -65,17 +65,17 @@
final Context mContext;
- private final Object mLock = new Object();
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ final Object mLock = new Object();
+ final Handler mHandler = new Handler(Looper.getMainLooper());
@GuardedBy("mLock")
private final SparseIntArray mFullUserIds = new SparseIntArray();
@GuardedBy("mLock")
private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
- private final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
+ final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
@GuardedBy("mLock")
- private final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
+ final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
final NotificationManager mNotificationManager;
public MediaCommunicationService(Context context) {
@@ -111,14 +111,14 @@
FullUserRecord user = getFullUserRecordLocked(userId);
if (user != null) {
if (user.getFullUserId() == userId) {
- user.destroySessionsForUserLocked(UserHandle.ALL.getIdentifier());
+ user.destroyAllSessions();
mUserRecords.remove(userId);
} else {
- user.destroySessionsForUserLocked(userId);
+ user.destroySessionsForUser(userId);
}
}
- updateUser();
}
+ updateUser();
}
@Nullable
@@ -134,6 +134,22 @@
return null;
}
+ List<Session2Token> getSession2TokensLocked(int userId) {
+ List<Session2Token> list = new ArrayList<>();
+ if (userId == ALL.getIdentifier()) {
+ int size = mUserRecords.size();
+ for (int i = 0; i < size; i++) {
+ list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
+ }
+ } else {
+ FullUserRecord user = getFullUserRecordLocked(userId);
+ if (user != null) {
+ list.addAll(user.getSession2Tokens(userId));
+ }
+ }
+ return list;
+ }
+
private FullUserRecord getFullUserRecordLocked(int userId) {
int fullUserId = mFullUserIds.get(userId, -1);
if (fullUserId < 0) {
@@ -188,27 +204,54 @@
}
}
- void dispatchSessionCreated(Session2Token token) {
- for (CallbackRecord record : mCallbackRecords) {
- if (record.mUserId != ALL.getIdentifier()
- && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
- continue;
- }
- try {
- record.mCallback.onSession2Created(token);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
-
- void onSessionDied(Session2Record record) {
+ void dispatchSession2Created(Session2Token token) {
synchronized (mLock) {
- destroySessionLocked(record);
+ for (CallbackRecord record : mCallbackRecords) {
+ if (record.mUserId != ALL.getIdentifier()
+ && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
+ continue;
+ }
+ try {
+ record.mCallback.onSession2Created(token);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session2 token created " + record);
+ }
+ }
}
}
- private void destroySessionLocked(Session2Record session) {
+ void dispatchSession2Changed(int userId) {
+ MediaParceledListSlice<Session2Token> allSession2Tokens;
+ MediaParceledListSlice<Session2Token> userSession2Tokens;
+
+ synchronized (mLock) {
+ allSession2Tokens =
+ new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
+ userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
+ }
+ allSession2Tokens.setInlineCountLimit(1);
+ userSession2Tokens.setInlineCountLimit(1);
+
+ synchronized (mLock) {
+ for (CallbackRecord record : mCallbackRecords) {
+ if (record.mUserId == ALL.getIdentifier()) {
+ try {
+ record.mCallback.onSession2Changed(allSession2Tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+ }
+ } else if (record.mUserId == userId) {
+ try {
+ record.mCallback.onSession2Changed(userSession2Tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+ }
+ }
+ }
+ }
+ }
+
+ void onSessionDied(Session2Record session) {
if (DEBUG) {
Log.d(TAG, "Destroying " + session);
}
@@ -217,12 +260,10 @@
return;
}
- FullUserRecord user = getFullUserRecordLocked(session.getUserId());
-
+ FullUserRecord user = session.getFullUser();
if (user != null) {
user.removeSession(session);
}
-
session.close();
}
@@ -241,17 +282,17 @@
throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+ " but actually=" + sessionToken.getUid());
}
+ FullUserRecord user;
+ int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
synchronized (mLock) {
- int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
- FullUserRecord user = getFullUserRecordLocked(userId);
- if (user == null) {
- Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
- return;
- }
- user.addSession(new Session2Record(MediaCommunicationService.this,
- sessionToken, mRecordExecutor));
- mHandler.post(() -> dispatchSessionCreated(sessionToken));
+ user = getFullUserRecordLocked(userId);
}
+ if (user == null) {
+ Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
+ return;
+ }
+ user.addSession(new Session2Record(MediaCommunicationService.this,
+ user, sessionToken, mRecordExecutor));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -299,10 +340,11 @@
int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
List<Session2Token> result;
synchronized (mLock) {
- FullUserRecord user = getFullUserRecordLocked(userId);
- result = user.getSession2Tokens(resolvedUserId);
+ result = getSession2TokensLocked(resolvedUserId);
}
- return new MediaParceledListSlice(result);
+ MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
+ parceledListSlice.setInlineCountLimit(1);
+ return parceledListSlice;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -427,7 +469,8 @@
final class FullUserRecord {
private final int mFullUserId;
- /** Sorted list of media sessions */
+ private final Object mUserLock = new Object();
+ @GuardedBy("mUserLock")
private final List<Session2Record> mSessionRecords = new ArrayList<>();
FullUserRecord(int fullUserId) {
@@ -435,11 +478,18 @@
}
public void addSession(Session2Record record) {
- mSessionRecords.add(record);
+ synchronized (mUserLock) {
+ mSessionRecords.add(record);
+ }
+ mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
}
- public void removeSession(Session2Record record) {
- mSessionRecords.remove(record);
+ private void removeSession(Session2Record record) {
+ synchronized (mUserLock) {
+ mSessionRecords.remove(record);
+ }
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
//TODO: Handle if the removed session was the media button session.
}
@@ -447,42 +497,68 @@
return mFullUserId;
}
- public List<Session2Token> getSession2Tokens(int userId) {
- return mSessionRecords.stream()
- .filter(record -> record.isActive()
- && (userId == UserHandle.ALL.getIdentifier()
- || record.getUserId() == userId))
- .map(Session2Record::getSessionToken)
- .collect(Collectors.toList());
+ public List<Session2Token> getAllSession2Tokens() {
+ synchronized (mUserLock) {
+ return mSessionRecords.stream()
+ .map(Session2Record::getSessionToken)
+ .collect(Collectors.toList());
+ }
}
- public void destroySessionsForUserLocked(int userId) {
- synchronized (mLock) {
- for (Session2Record record : mSessionRecords) {
- if (userId == UserHandle.ALL.getIdentifier()
- || record.getUserId() == userId) {
- destroySessionLocked(record);
+ public List<Session2Token> getSession2Tokens(int userId) {
+ synchronized (mUserLock) {
+ return mSessionRecords.stream()
+ .filter(record -> record.getUserId() == userId)
+ .map(Session2Record::getSessionToken)
+ .collect(Collectors.toList());
+ }
+ }
+
+ public void destroyAllSessions() {
+ synchronized (mUserLock) {
+ for (Session2Record session : mSessionRecords) {
+ session.close();
+ }
+ mSessionRecords.clear();
+ }
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+ }
+
+ public void destroySessionsForUser(int userId) {
+ boolean changed = false;
+ synchronized (mUserLock) {
+ for (int i = mSessionRecords.size() - 1; i >= 0; i--) {
+ Session2Record session = mSessionRecords.get(i);
+ if (session.getUserId() == userId) {
+ mSessionRecords.remove(i);
+ session.close();
+ changed = true;
}
}
}
+ if (changed) {
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+ }
}
}
static final class Session2Record {
- private final Session2Token mSessionToken;
- private final Object mLock = new Object();
- private final WeakReference<MediaCommunicationService> mServiceRef;
- @GuardedBy("mLock")
+ final Session2Token mSessionToken;
+ final Object mSession2RecordLock = new Object();
+ final WeakReference<MediaCommunicationService> mServiceRef;
+ final WeakReference<FullUserRecord> mFullUserRef;
+ @GuardedBy("mSession2RecordLock")
private final MediaController2 mController;
- @GuardedBy("mLock")
- private boolean mIsConnected;
- @GuardedBy("mLock")
+ @GuardedBy("mSession2RecordLock")
+ boolean mIsConnected;
+ @GuardedBy("mSession2RecordLock")
private boolean mIsClosed;
- Session2Record(MediaCommunicationService service, Session2Token token,
- Executor controllerExecutor) {
+ Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
+ Session2Token token, Executor controllerExecutor) {
mServiceRef = new WeakReference<>(service);
+ mFullUserRef = new WeakReference<>(fullUser);
mSessionToken = token;
mController = new MediaController2.Builder(service.getContext(), token)
.setControllerCallback(controllerExecutor, new Controller2Callback())
@@ -493,23 +569,19 @@
return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
}
- public boolean isActive() {
- synchronized (mLock) {
- return mIsConnected;
- }
+ public FullUserRecord getFullUser() {
+ return mFullUserRef.get();
}
public boolean isClosed() {
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
return mIsClosed;
}
}
public void close() {
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
mIsClosed = true;
- // Call close regardless of the mIsConnected. This may be called when it's not yet
- // connected.
mController.close();
}
}
@@ -525,13 +597,9 @@
if (DEBUG) {
Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
}
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
mIsConnected = true;
}
- MediaCommunicationService service = mServiceRef.get();
- if (service != null) {
- //TODO: notify session state changed
- }
}
@Override
@@ -539,7 +607,7 @@
if (DEBUG) {
Log.d(TAG, "disconnected from " + mSessionToken);
}
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
mIsConnected = false;
}
MediaCommunicationService service = mServiceRef.get();
diff --git a/core/api/current.txt b/core/api/current.txt
index 239ba65..2bada80 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -27110,7 +27110,7 @@
method @NonNull public int[] getExposedCapabilities();
method @NonNull public String getGatewayConnectionName();
method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
- method @NonNull public long[] getRetryIntervalsMs();
+ method @NonNull public long[] getRetryIntervalsMillis();
}
public static final class VcnGatewayConnectionConfig.Builder {
@@ -27119,7 +27119,7 @@
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMs(@NonNull long[]);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
}
public class VcnManager {
@@ -41990,7 +41990,6 @@
method @IntRange(from=1, to=261) public int getBand();
method @IntRange(from=1) public int getCellBandwidthDownlinkKhz();
method @IntRange(from=1) public int getCellBandwidthUplinkKhz();
- method @Deprecated public int getChannelNumber();
method public int getConnectionStatus();
method @IntRange(from=0) public int getDownlinkChannelNumber();
method @IntRange(from=0) public int getDownlinkFrequencyKhz();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 677da39..e48a1da 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -223,9 +223,9 @@
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
- method @NonNull public String getIface();
+ method @NonNull public String getInterface();
method public int getOwnerUid();
- method @NonNull public java.util.List<java.lang.String> getUnderlyingIfaces();
+ method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 22af3f7..3537d8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -317,6 +317,7 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int durationBetweenRequestsMillis;
field public static final int hotwordDetectionService;
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -325,7 +326,6 @@
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
- field public static final int throttleDurationMillis;
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -1448,11 +1448,12 @@
package android.app.search {
public final class Query implements android.os.Parcelable {
- ctor public Query(@NonNull String, long, @Nullable android.os.Bundle);
+ ctor public Query(@NonNull String, long, @NonNull android.os.Bundle);
+ ctor public Query(@NonNull String, long);
method public int describeContents();
- method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getInput();
- method @NonNull public long getTimestamp();
+ method public long getTimestampMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.search.Query> CREATOR;
}
@@ -1485,9 +1486,10 @@
}
public final class SearchContext implements android.os.Parcelable {
- ctor public SearchContext(int, int, @Nullable android.os.Bundle);
+ ctor public SearchContext(int, int);
+ ctor public SearchContext(int, int, @NonNull android.os.Bundle);
method public int describeContents();
- method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public android.os.Bundle getExtras();
method @Nullable public String getPackageName();
method @NonNull public int getResultTypes();
method @NonNull public int getTimeoutMillis();
@@ -1497,7 +1499,6 @@
public final class SearchSession implements java.lang.AutoCloseable {
method public void close();
- method public void destroy();
method protected void finalize();
method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
@@ -1512,7 +1513,7 @@
public final class SearchTarget implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
- method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getId();
method @NonNull public String getLayoutType();
method @NonNull public String getPackageName();
@@ -1526,13 +1527,17 @@
method public boolean shouldHide();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.search.SearchTarget> CREATOR;
+ field public static final int RESULT_TYPE_APPLICATION = 1; // 0x1
+ field public static final int RESULT_TYPE_SHORTCUT = 2; // 0x2
+ field public static final int RESULT_TYPE_SLICE = 4; // 0x4
+ field public static final int RESULT_TYPE_WIDGETS = 8; // 0x8
}
public static final class SearchTarget.Builder {
ctor public SearchTarget.Builder(int, @NonNull String, @NonNull String);
method @NonNull public android.app.search.SearchTarget build();
method @NonNull public android.app.search.SearchTarget.Builder setAppWidgetProviderInfo(@NonNull android.appwidget.AppWidgetProviderInfo);
- method @NonNull public android.app.search.SearchTarget.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.app.search.SearchTarget.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.app.search.SearchTarget.Builder setPackageName(@NonNull String);
method @NonNull public android.app.search.SearchTarget.Builder setParentId(@NonNull String);
method @NonNull public android.app.search.SearchTarget.Builder setScore(float);
@@ -9867,7 +9872,7 @@
public final class DisplayHashParams implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.util.Size getBufferSize();
- method public boolean isUseGrayscale();
+ method public boolean isGrayscaleBuffer();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
}
@@ -9876,7 +9881,7 @@
ctor public DisplayHashParams.Builder();
method @NonNull public android.service.displayhash.DisplayHashParams build();
method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
- method @NonNull public android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
}
public abstract class DisplayHashingService extends android.app.Service {
@@ -10260,10 +10265,10 @@
public abstract class SearchUiService extends android.app.Service {
ctor public SearchUiService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId);
method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
+ method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
}
}
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index f366a54..65a8780 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,18 @@
}
+package android.app.search {
+
+ public final class Query implements android.os.Parcelable {
+ method @Deprecated @NonNull public long getTimestamp();
+ }
+
+ public final class SearchSession implements java.lang.AutoCloseable {
+ method @Deprecated public void destroy();
+ }
+
+}
+
package android.bluetooth {
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -163,6 +175,14 @@
}
+package android.service.search {
+
+ public abstract class SearchUiService extends android.app.Service {
+ method @Deprecated public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
+ }
+
+}
+
package android.telecom {
public class TelecomManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a24555f..6d2d023 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1947,8 +1947,9 @@
pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
pw.print(" activityType="); pw.print(activityTypeToString(getActivityType()));
pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode()));
- pw.print(" supportsSplitScreenMultiWindow=");
- pw.println(supportsSplitScreenMultiWindow);
+ pw.print(" supportsSplitScreenMultiWindow="); pw.print(supportsSplitScreenMultiWindow);
+ pw.print(" supportsMultiWindow=");
+ pw.println(supportsMultiWindow);
if (taskDescription != null) {
pw.print(" ");
final ActivityManager.TaskDescription td = taskDescription;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6df9f4d..4a7fcd2 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -349,20 +349,6 @@
}
/**
- * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
- * if the request orientation is not met, and will be shown in size-compat mode if the container
- * size has changed.
- * @hide
- */
- public static boolean supportsNonResizableMultiWindow() {
- try {
- return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
* @hide
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 656942d..9ce37e4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3273,6 +3273,13 @@
dir = null;
}
}
+ if (dir != null && !dir.canWrite()) {
+ // Older versions of the MediaProvider mainline module had a rare early boot race
+ // condition where app-private dirs could be created with the wrong permissions;
+ // fix this up here. This check should be very fast, because dir.exists() above
+ // will already have loaded the dentry in the cache.
+ sm.fixupAppDir(dir);
+ }
result[i] = dir;
}
return result;
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b75e89c..74d51a0 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -259,13 +259,6 @@
void setSplitScreenResizing(boolean resizing);
boolean supportsLocalVoiceInteraction();
- /**
- * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
- * if the request orientation is not met, and will be shown in size-compat mode if the container
- * size has changed.
- */
- boolean supportsNonResizableMultiWindow();
-
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 6ad5eea..b95412f 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -138,6 +138,13 @@
public boolean supportsSplitScreenMultiWindow;
/**
+ * Whether this task supports multi windowing modes based on the device settings and the
+ * root activity resizability and configuration.
+ * @hide
+ */
+ public boolean supportsMultiWindow;
+
+ /**
* The resize mode of the task. See {@link ActivityInfo#resizeMode}.
* @hide
*/
@@ -329,6 +336,7 @@
}
return topActivityType == that.topActivityType
&& isResizeable == that.isResizeable
+ && supportsMultiWindow == that.supportsMultiWindow
&& Objects.equals(positionInParent, that.positionInParent)
&& Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
&& getWindowingMode() == that.getWindowingMode()
@@ -375,6 +383,7 @@
taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
supportsSplitScreenMultiWindow = source.readBoolean();
+ supportsMultiWindow = source.readBoolean();
resizeMode = source.readInt();
configuration.readFromParcel(source);
token = WindowContainerToken.CREATOR.createFromParcel(source);
@@ -412,6 +421,7 @@
dest.writeTypedObject(taskDescription, flags);
dest.writeBoolean(supportsSplitScreenMultiWindow);
+ dest.writeBoolean(supportsMultiWindow);
dest.writeInt(resizeMode);
configuration.writeToParcel(dest, flags);
token.writeToParcel(dest, flags);
@@ -440,6 +450,7 @@
+ " numActivities=" + numActivities
+ " lastActiveTime=" + lastActiveTime
+ " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+ + " supportsMultiWindow=" + supportsMultiWindow
+ " resizeMode=" + resizeMode
+ " isResizeable=" + isResizeable
+ " token=" + token
diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java
index 3ab20bb..34ace48 100644
--- a/core/java/android/app/search/Query.java
+++ b/core/java/android/app/search/Query.java
@@ -16,63 +16,108 @@
package android.app.search;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/**
+ * Query object is sent over from client to the service.
+ *
+ * Inside the query object, there is a timestamp that trackes when the query string was typed.
+ *
+ * If this object was created for the {@link SearchSession#query},
+ * the client expects first consumer to be returned
+ * within {@link #getTimestampMillis()} + {@link SearchContext#getTimeoutMillis()}
+ * Base of the timestamp should be SystemClock.elasedRealTime()
+ *
* @hide
*/
@SystemApi
public final class Query implements Parcelable {
/**
- * Query string typed from the client.
+ * string typed from the client.
*/
@NonNull
private final String mInput;
- /**
- * The timestamp that the query string was typed. If this object was created for the
- * {@link SearchSession#query}, the client expects first consumer to be returned
- * within mTimestamp + {@link SearchContext#mTimeoutMillis}
- */
- private final long mTimestamp;
+ private final long mTimestampMillis;
- @Nullable
+ /**
+ * Contains other client UI constraints related data
+ */
+ @NonNull
private final Bundle mExtras;
+ /**
+ * Query object used to pass search box input from client to service.
+ *
+ * @param input string typed from the client
+ * @param timestampMillis timestamp that query string was typed.
+ * @param extras bundle that contains other client UI constraints data
+ */
public Query(@NonNull String input,
- long timestamp,
- @SuppressLint("NullableCollection")
- @Nullable Bundle extras) {
+ long timestampMillis,
+ @NonNull Bundle extras) {
mInput = input;
- mTimestamp = timestamp;
- mExtras = extras;
+ mTimestampMillis = timestampMillis;
+ mExtras = extras == null ? extras : new Bundle();
+ }
+
+ /**
+ * Query object used to pass search box input from client to service.
+ *
+ * @param input string typed from the client
+ * @param timestampMillis timestamp that query string was typed
+ */
+ public Query(@NonNull String input, long timestampMillis) {
+ this(input, timestampMillis, new Bundle());
}
private Query(Parcel parcel) {
mInput = parcel.readString();
- mTimestamp = parcel.readLong();
+ mTimestampMillis = parcel.readLong();
mExtras = parcel.readBundle();
}
+ /**
+ * @return string typed from the client
+ */
@NonNull
public String getInput() {
return mInput;
}
+ /**
+ * @deprecated Will be replaced by {@link #getTimestampMillis()} as soon as
+ * new SDK is adopted.
+ *
+ * @removed
+ */
+ @Deprecated
@NonNull
public long getTimestamp() {
- return mTimestamp;
+ return mTimestampMillis;
}
- @Nullable
- @SuppressLint("NullableCollection")
+ /**
+ * Base of the timestamp should be SystemClock.elasedRealTime()
+ *
+ * @return timestamp that query string was typed
+ */
+ public long getTimestampMillis() {
+ return mTimestampMillis;
+ }
+
+ /**
+ * @return bundle that contains other client constraints related to the query
+ */
+ @NonNull
public Bundle getExtras() {
+ if (mExtras == null) {
+ return new Bundle();
+ }
return mExtras;
}
@@ -84,7 +129,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mInput);
- dest.writeLong(mTimestamp);
+ dest.writeLong(mTimestampMillis);
dest.writeBundle(mExtras);
}
diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java
index 548b7da..3e345fa 100644
--- a/core/java/android/app/search/SearchContext.java
+++ b/core/java/android/app/search/SearchContext.java
@@ -17,13 +17,20 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
/**
+ * When {@link SearchSession} is created, {@link SearchContext} object is created
+ * to pass the result types from the {@link SearchSession#query(Query, Executor, Consumer)}
+ * method that the client wants.
+ *
* @hide
*/
@SystemApi
@@ -51,12 +58,25 @@
@Nullable
private String mPackageName;
+ /**
+ * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+ * @param timeoutMillis timeout before client renders its own fallback result
+ */
+ public SearchContext(int resultTypes, int timeoutMillis) {
+ this(resultTypes, timeoutMillis, new Bundle());
+ }
+
+ /**
+ * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+ * @param timeoutMillis timeout before client renders its own fallback result
+ * @param extras other client constraints (e.g., height of the search surface)
+ */
public SearchContext(int resultTypes,
- int queryTimeoutMillis,
- @SuppressLint("NullableCollection") @Nullable Bundle extras) {
+ int timeoutMillis,
+ @NonNull Bundle extras) {
mResultTypes = resultTypes;
- mTimeoutMillis = queryTimeoutMillis;
- mExtras = extras;
+ mTimeoutMillis = timeoutMillis;
+ mExtras = Objects.requireNonNull(extras);
}
private SearchContext(Parcel parcel) {
@@ -74,7 +94,7 @@
/**
* @hide
*/
- public void setPackageName(@Nullable String packageName) {
+ void setPackageName(@Nullable String packageName) {
mPackageName = packageName;
}
@@ -83,8 +103,7 @@
return mTimeoutMillis;
}
- @Nullable
- @SuppressLint("NullableCollection")
+ @NonNull
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 7bd88d9..a5425a2 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -37,7 +37,9 @@
import java.util.function.Consumer;
/**
- * Client API to share information about the search UI state and execute query.
+ * Client needs to create {@link SearchSession} object from in order to execute
+ * {@link #query(Query, Executor, Consumer)} method and share client side signals
+ * back to the service using {@link #notifyEvent(Query, SearchTargetEvent)}.
*
* <p>
* Usage: <pre> {@code
@@ -60,7 +62,7 @@
* }
*
* void onDestroy() {
- * mSearchSession.destroy();
+ * mSearchSession.close();
* }
*
* }</pre>
@@ -108,7 +110,10 @@
}
/**
- * Notifies the search service of an search target event.
+ * Notifies the search service of an search target event (e.g., user interaction
+ * and lifecycle event of the search surface).
+ *
+ * {@see SearchTargetEvent}
*
* @param query input object associated with the event.
* @param event The {@link SearchTargetEvent} that represents the search target event.
@@ -153,7 +158,11 @@
/**
* Destroys the client and unregisters the callback. Any method on this class after this call
* will throw {@link IllegalStateException}.
+ *
+ * @deprecated
+ * @removed
*/
+ @Deprecated
public void destroy() {
if (!mIsClosed.getAndSet(true)) {
mCloseGuard.close();
@@ -188,6 +197,11 @@
}
}
+ /**
+ * Destroys the client and unregisters the callback. Any method on this class after this call
+ * will throw {@link IllegalStateException}.
+ *
+ */
@Override
public void close() {
try {
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index 6a80f8b..56c5ddf 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -15,34 +15,73 @@
*/
package android.app.search;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.app.slice.SliceManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * A representation of a searchable item info.
+ * A representation of a search result. Search result can be expressed in one of the following:
+ * app icon, shortcut, slice, widget, or a custom object using {@link SearchAction}. While
+ * app icon ({@link PackageManager}, shortcut {@link ShortcutManager}, slice {@link SliceManager},
+ * or widget (@link AppWidgetManager} are published content backed by the system service,
+ * {@link SearchAction} is a custom object that the service can use to send search result to the
+ * client.
+ *
+ * These various types of Android primitives could be defined as {@link SearchResultType}. Some
+ * times, the result type can define the layout type that that this object can be rendered in.
+ * (e.g., app widget). Most times, {@link #getLayoutType()} assigned by the service
+ * can recommend which layout this target should be rendered in.
+ *
+ * The service can also use fields such as {@link #getScore()} to indicate
+ * how confidence the search result is and {@link #shouldHide()} to indicate
+ * whether it is recommended to be shown by default.
+ *
+ * Finally, {@link #getId()} is the unique identifier of this search target and a single
+ * search target is defined by being able to express a single launcheable item. In case the
+ * service want to recommend how to combine multiple search target objects to render in a group
+ * (e.g., same row), {@link #getParentId()} can be assigned on the sub targets of the group
+ * using the primary search target's identifier.
*
* @hide
*/
@SystemApi
public final class SearchTarget implements Parcelable {
-
- @NonNull
+ public static final int RESULT_TYPE_APPLICATION = 1 << 0;
+ public static final int RESULT_TYPE_SHORTCUT = 1 << 1;
+ public static final int RESULT_TYPE_SLICE = 1 << 2;
+ public static final int RESULT_TYPE_WIDGETS = 1 << 3;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"RESULT_TYPE_"}, value = {
+ RESULT_TYPE_APPLICATION,
+ RESULT_TYPE_SHORTCUT,
+ RESULT_TYPE_SLICE,
+ RESULT_TYPE_WIDGETS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SearchResultType {}
private final int mResultType;
/**
- * Constant to express how the group of {@link SearchTarget} should be laid out.
+ * Constant to express how the group of {@link SearchTarget} should be rendered on
+ * the client side. (e.g., "icon", "icon_row", "short_icon_row")
*/
@NonNull
private final String mLayoutType;
@@ -69,13 +108,13 @@
private final AppWidgetProviderInfo mAppWidgetProviderInfo;
@Nullable
private final Uri mSliceUri;
- @Nullable
+
+ @NonNull
private final Bundle mExtras;
private SearchTarget(Parcel parcel) {
mResultType = parcel.readInt();
mLayoutType = parcel.readString();
-
mId = parcel.readString();
mParentId = parcel.readString();
mScore = parcel.readFloat();
@@ -102,7 +141,7 @@
@Nullable ShortcutInfo shortcutInfo,
@Nullable Uri sliceUri,
@Nullable AppWidgetProviderInfo appWidgetProviderInfo,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
mResultType = resultType;
mLayoutType = Objects.requireNonNull(layoutType);
mId = Objects.requireNonNull(id);
@@ -129,9 +168,9 @@
}
/**
- * Retrieves the result type.
+ * Retrieves the result type {@see SearchResultType}.
*/
- public int getResultType() {
+ public @SearchResultType int getResultType() {
return mResultType;
}
@@ -167,7 +206,7 @@
}
/**
- * TODO: add comment
+ * Indicates whether this object should be hidden and shown only on demand.
*/
public boolean shouldHide() {
return mShouldHide;
@@ -198,7 +237,7 @@
}
/**
- * Return widget provider info.
+ * Return a widget provider info.
*/
@Nullable
public AppWidgetProviderInfo getAppWidgetProviderInfo() {
@@ -206,7 +245,7 @@
}
/**
- * Return slice uri.
+ * Returns a slice uri.
*/
@Nullable
public Uri getSliceUri() {
@@ -214,7 +253,7 @@
}
/**
- * Return search action.
+ * Returns a search action.
*/
@Nullable
public SearchAction getSearchAction() {
@@ -224,8 +263,7 @@
/**
* Return extra bundle.
*/
- @Nullable
- @SuppressLint("NullableCollection")
+ @NonNull
public Bundle getExtras() {
return mExtras;
}
@@ -295,10 +333,10 @@
private Uri mSliceUri;
@Nullable
private AppWidgetProviderInfo mAppWidgetProviderInfo;
- @Nullable
+ @NonNull
private Bundle mExtras;
- public Builder(int resultType,
+ public Builder(@SearchResultType int resultType,
@NonNull String layoutType,
@NonNull String id) {
mId = id;
@@ -369,32 +407,30 @@
*/
@NonNull
public Builder setSliceUri(@NonNull Uri sliceUri) {
- // TODO: add packageName check
mSliceUri = sliceUri;
return this;
}
/**
- * TODO: add comment
+ * Set the {@link SearchAction} object to this target.
*/
@NonNull
- public Builder setSearchAction(@Nullable SearchAction remoteAction) {
- // TODO: add packageName check
- mSearchAction = remoteAction;
+ public Builder setSearchAction(@Nullable SearchAction searchAction) {
+ mSearchAction = searchAction;
return this;
}
/**
- * TODO: add comment
+ * Set any extra information that needs to be shared between service and the client.
*/
@NonNull
- public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
- mExtras = extras;
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = Objects.requireNonNull(extras);
return this;
}
/**
- * TODO: add comment
+ * Sets the score of the object.
*/
@NonNull
public Builder setScore(float score) {
@@ -403,7 +439,7 @@
}
/**
- * TODO: add comment
+ * Sets whether the result should be hidden by default inside client.
*/
@NonNull
public Builder setShouldHide(boolean shouldHide) {
diff --git a/core/java/android/app/search/SearchTargetEvent.java b/core/java/android/app/search/SearchTargetEvent.java
index f478dc3..d4915af 100644
--- a/core/java/android/app/search/SearchTargetEvent.java
+++ b/core/java/android/app/search/SearchTargetEvent.java
@@ -29,7 +29,11 @@
import java.util.Objects;
/**
- * A representation of an app target event.
+ * A representation of an search target event.
+ *
+ * There are two types of events. First type of event correspends to the user interaction
+ * that happens on the search surface. (e.g., {@link #ACTION_TAP}. Second type of events
+ * correspends to the lifecycle event of the search surface {@link #ACTION_SURFACE_VISIBLE}.
*
* @hide
*/
diff --git a/core/java/android/app/search/SearchUiManager.java b/core/java/android/app/search/SearchUiManager.java
index 636bfe1..ce6d8b2 100644
--- a/core/java/android/app/search/SearchUiManager.java
+++ b/core/java/android/app/search/SearchUiManager.java
@@ -25,6 +25,12 @@
/**
* Class that provides methods to create search ui session clients.
*
+ * Usage: <pre> {@code
+ * mSearchUiManager = context.getSystemService(SearchUiManager.class);
+ * mSearchSession.createSearchSession(searchContext)
+ *
+ * }</pre>
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 7e1df1b..114ad87 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1885,9 +1885,9 @@
* in {@link android.provider.MediaStore.MediaColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "rw" for read and write access, or "rwt" for read and write access
- * that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
*
* @return Returns a new ParcelFileDescriptor which you can use to access
* the file.
@@ -1948,10 +1948,9 @@
* in {@link android.provider.MediaStore.MediaColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "w" for write-only access, "rw" for read and write access, or
- * "rwt" for read and write access that truncates any existing
- * file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2011,11 +2010,9 @@
* containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "w" for write-only access (erasing whatever data is currently in
- * the file), "wa" for write-only access to append to any existing data,
- * "rw" for read and write access on any existing data, and "rwt" for read
- * and write access that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
*
* @return Returns a new AssetFileDescriptor which you can use to access
* the file.
@@ -2068,11 +2065,9 @@
* containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "w" for write-only access (erasing whatever data is currently in
- * the file), "wa" for write-only access to append to any existing data,
- * "rw" for read and write access on any existing data, and "rwt" for read
- * and write access that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2103,11 +2098,9 @@
* by looking up a column named "_data" at the given URI.
*
* @param uri The URI to be opened.
- * @param mode The file mode. May be "r" for read-only access,
- * "w" for write-only access (erasing whatever data is currently in
- * the file), "wa" for write-only access to append to any existing data,
- * "rw" for read and write access on any existing data, and "rwt" for read
- * and write access that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
*
* @return Returns a new ParcelFileDescriptor that can be used by the
* client to access the file.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index aec39da..1132991 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,9 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.system.ErrnoException;
import android.system.Int64Ref;
+import android.system.Os;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -76,8 +78,10 @@
import dalvik.system.CloseGuard;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -864,6 +868,20 @@
return wrap((ContentInterface) wrapped);
}
+ /**
+ * Offer to locally truncate the given file when opened using the write-only
+ * mode. This is typically used to preserve legacy compatibility behavior.
+ */
+ private static void maybeTruncate(FileDescriptor fd, String mode) throws FileNotFoundException {
+ if ("w".equals(mode)) {
+ try {
+ Os.ftruncate(fd, 0);
+ } catch (ErrnoException e) {
+ throw new FileNotFoundException("Failed to truncate: " + e.getMessage());
+ }
+ }
+ }
+
/** @hide */
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
@@ -1525,8 +1543,20 @@
}
/**
- * Synonym for {@link #openOutputStream(Uri, String)
- * openOutputStream(uri, "w")}.
+ * Open a stream on to the content associated with a content URI. If there
+ * is no data associated with the URI, FileNotFoundException is thrown.
+ *
+ * <h5>Accepts the following URI schemes:</h5>
+ * <ul>
+ * <li>content ({@link #SCHEME_CONTENT})</li>
+ * <li>file ({@link #SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ *
+ * <p>This method behaves like {@link FileOutputStream} and automatically
+ * truncates any existing contents.
*
* @param uri The desired URI.
* @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1534,7 +1564,16 @@
*/
public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
throws FileNotFoundException {
- return openOutputStream(uri, "w");
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, "w", null);
+ if (fd == null) return null;
+ try {
+ final FileOutputStream res = fd.createOutputStream();
+ // Unconditionally truncate to mirror FileOutputStream behavior
+ maybeTruncate(res.getFD(), "w");
+ return res;
+ } catch (IOException e) {
+ throw new FileNotFoundException("Unable to create stream");
+ }
}
/**
@@ -1551,7 +1590,9 @@
* on these schemes.
*
* @param uri The desired URI.
- * @param mode May be "w", "wa", "rw", or "rwt".
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @return an OutputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #openAssetFileDescriptor(Uri, String)
@@ -1559,8 +1600,14 @@
public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
+ if (fd == null) return null;
try {
- return fd != null ? fd.createOutputStream() : null;
+ final FileOutputStream res = fd.createOutputStream();
+ // Preserve legacy behavior by offering to truncate
+ if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+ maybeTruncate(res.getFD(), mode);
+ }
+ return res;
} catch (IOException e) {
throw new FileNotFoundException("Unable to create stream");
}
@@ -1607,8 +1654,9 @@
* provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openFile
- * ContentProvider.openFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1650,8 +1698,9 @@
* provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openFile
- * ContentProvider.openFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param cancellationSignal A signal to cancel the operation in progress,
* or null if none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
@@ -1744,8 +1793,9 @@
* from any built-in data conversion that a provider implements.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
- * ContentProvider.openAssetFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1798,8 +1848,9 @@
* from any built-in data conversion that a provider implements.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
- * ContentProvider.openAssetFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param cancellationSignal A signal to cancel the operation in progress, or null if
* none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
@@ -1835,6 +1886,10 @@
} else if (SCHEME_FILE.equals(scheme)) {
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
+ // Preserve legacy behavior by offering to truncate
+ if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+ maybeTruncate(pfd.getFileDescriptor(), mode);
+ }
return new AssetFileDescriptor(pfd, 0, -1);
} else {
if ("r".equals(mode)) {
@@ -1892,6 +1947,11 @@
// ParcelFileDescriptorInner do that when it is closed.
stableProvider = null;
+ // Preserve legacy behavior by offering to truncate
+ if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+ maybeTruncate(pfd.getFileDescriptor(), mode);
+ }
+
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b498325..7c7cfdb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6397,7 +6397,9 @@
public static final int FLAG_ACTIVITY_NO_HISTORY = 0x40000000;
/**
* If set, the activity will not be launched if it is already running
- * at the top of the history stack.
+ * at the top of the history stack. See
+ * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html#TaskLaunchModes">
+ * Tasks and Back Stack</a> for more information.
*/
public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
/**
@@ -6537,8 +6539,7 @@
public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
/**
* This flag is not normally set by application code, but set for you by
- * the system if this activity is being launched from history
- * (longpress home key).
+ * the system if this activity is being launched from history.
*/
public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
/**
@@ -6571,7 +6572,9 @@
* equivalent of the Activity manifest specifying {@link
* android.R.attr#documentLaunchMode}="intoExisting". When used with
* FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
- * {@link android.R.attr#documentLaunchMode}="always".
+ * {@link android.R.attr#documentLaunchMode}="always". The flag is ignored even in
+ * conjunction with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} when the Activity manifest specifies
+ * {@link android.R.attr#documentLaunchMode}="never".
*
* Refer to {@link android.R.attr#documentLaunchMode} for more information.
*
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 48d3686..d7b96df 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2312,7 +2312,7 @@
if (setVisible) {
cancelImeSurfaceRemoval();
}
- mPrivOps.applyImeVisibility(setVisible
+ mPrivOps.applyImeVisibilityAsync(setVisible
? mCurShowInputToken : mCurHideInputToken, setVisible);
}
@@ -3319,7 +3319,7 @@
if (mNotifyUserActionSent) {
return;
}
- mPrivOps.notifyUserAction();
+ mPrivOps.notifyUserActionAsync();
mNotifyUserActionSent = true;
}
}
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 459fdac..33f9375 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -71,13 +71,13 @@
/** Get the interface name of this network. */
@NonNull
- public String getIface() {
+ public String getInterface() {
return mIface;
}
/** Get the names of the interfaces underlying this network. */
@NonNull
- public List<String> getUnderlyingIfaces() {
+ public List<String> getUnderlyingInterfaces() {
return mUnderlyingIfaces;
}
@@ -124,8 +124,8 @@
if (!(o instanceof UnderlyingNetworkInfo)) return false;
final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
return mOwnerUid == that.getOwnerUid()
- && Objects.equals(mIface, that.getIface())
- && Objects.equals(mUnderlyingIfaces, that.getUnderlyingIfaces());
+ && Objects.equals(mIface, that.getInterface())
+ && Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
}
@Override
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index eedeeb5..f02346b 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -141,7 +141,7 @@
* <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
* than this value.
*
- * @see {@link Builder#setRetryIntervalsMs()}
+ * @see {@link Builder#setRetryIntervalsMillis()}
*/
private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
@@ -342,10 +342,10 @@
/**
* Retrieves the configured retry intervals.
*
- * @see Builder#setRetryIntervalsMs(long[])
+ * @see Builder#setRetryIntervalsMillis(long[])
*/
@NonNull
- public long[] getRetryIntervalsMs() {
+ public long[] getRetryIntervalsMillis() {
return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
}
@@ -555,7 +555,7 @@
* @see VcnManager for additional discussion on fail-safe mode
*/
@NonNull
- public Builder setRetryIntervalsMs(@NonNull long[] retryIntervalsMs) {
+ public Builder setRetryIntervalsMillis(@NonNull long[] retryIntervalsMs) {
validateRetryInterval(retryIntervalsMs);
mRetryIntervalsMs = retryIntervalsMs;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c084787..0c9ad1b 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -31,6 +31,7 @@
import static android.system.OsConstants.S_ISREG;
import static android.system.OsConstants.S_IWOTH;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -63,6 +64,8 @@
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UncheckedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.Socket;
import java.nio.ByteOrder;
@@ -110,6 +113,20 @@
private final CloseGuard mGuard = CloseGuard.get();
+ /** @hide */
+ @IntDef(prefix = {"MODE_"}, value = {
+ MODE_WORLD_READABLE,
+ MODE_WORLD_WRITEABLE,
+ MODE_READ_ONLY,
+ MODE_WRITE_ONLY,
+ MODE_READ_WRITE,
+ MODE_CREATE,
+ MODE_TRUNCATE,
+ MODE_APPEND,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Mode { }
+
/**
* For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
* this file doesn't already exist, then create the file with permissions
@@ -227,7 +244,8 @@
* be opened with the requested mode.
* @see #parseMode(String)
*/
- public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
+ public static ParcelFileDescriptor open(File file, @Mode int mode)
+ throws FileNotFoundException {
final FileDescriptor fd = openInternal(file, mode);
if (fd == null) return null;
@@ -259,7 +277,7 @@
// We can't accept a generic Executor here, since we need to use
// MessageQueue.addOnFileDescriptorEventListener()
@SuppressLint("ExecutorRegistration")
- public static ParcelFileDescriptor open(File file, int mode, Handler handler,
+ public static ParcelFileDescriptor open(File file, @Mode int mode, Handler handler,
final OnCloseListener listener) throws IOException {
if (handler == null) {
throw new IllegalArgumentException("Handler must not be null");
@@ -330,7 +348,8 @@
return pfd;
}
- private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
+ private static FileDescriptor openInternal(File file, @Mode int mode)
+ throws FileNotFoundException {
final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
int realMode = S_IRWXU | S_IRWXG;
@@ -623,15 +642,36 @@
}
/**
- * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
- * with {@link #open}.
+ * Converts a string representing a file mode, such as "rw", into a bitmask
+ * suitable for use with {@link #open}.
* <p>
- * @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt".
+ * The argument must define at least one of the following base access modes:
+ * <ul>
+ * <li>"r" indicates the file should be opened in read-only mode, equivalent
+ * to {@link OsConstants#O_RDONLY}.
+ * <li>"w" indicates the file should be opened in write-only mode,
+ * equivalent to {@link OsConstants#O_WRONLY}.
+ * <li>"rw" indicates the file should be opened in read-write mode,
+ * equivalent to {@link OsConstants#O_RDWR}.
+ * </ul>
+ * In addition to a base access mode, the following additional modes may
+ * requested:
+ * <ul>
+ * <li>"a" indicates the file should be opened in append mode, equivalent to
+ * {@link OsConstants#O_APPEND}. Before each write, the file offset is
+ * positioned at the end of the file.
+ * <li>"t" indicates the file should be opened in truncate mode, equivalent
+ * to {@link OsConstants#O_TRUNC}. If the file already exists and is a
+ * regular file and is opened for writing, it will be truncated to length 0.
+ * </ul>
+ *
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt".
* @return A bitmask representing the given file mode.
- * @throws IllegalArgumentException if the given string does not match a known file mode.
+ * @throws IllegalArgumentException if the given string does not match a
+ * known file mode.
*/
- public static int parseMode(String mode) {
+ public static @Mode int parseMode(String mode) {
return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 98abd96..c8e5e42 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8515,6 +8515,12 @@
"swipe_bottom_to_notification_enabled";
/**
+ * Controls whether One-Handed mode is currently activated.
+ * @hide
+ */
+ public static final String ONE_HANDED_MODE_ACTIVATED = "one_handed_mode_activated";
+
+ /**
* For user preference if One-Handed Mode enabled.
* @hide
*/
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
index 2ec9d5d..123c209 100644
--- a/core/java/android/service/displayhash/DisplayHashParams.java
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -48,7 +48,7 @@
/**
* Whether the content will be captured in grayscale or color.
*/
- private final boolean mUseGrayscale;
+ private final boolean mGrayscaleBuffer;
/**
* A builder for {@link DisplayHashParams}
@@ -56,7 +56,7 @@
public static final class Builder {
@Nullable
private Size mBufferSize;
- private boolean mUseGrayscale;
+ private boolean mGrayscaleBuffer;
/**
* Creates a new Builder.
@@ -77,15 +77,15 @@
* Whether the content will be captured in grayscale or color.
*/
@NonNull
- public Builder setUseGrayscale(boolean useGrayscale) {
- mUseGrayscale = useGrayscale;
+ public Builder setGrayscaleBuffer(boolean grayscaleBuffer) {
+ mGrayscaleBuffer = grayscaleBuffer;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
@NonNull
public DisplayHashParams build() {
- return new DisplayHashParams(mBufferSize, mUseGrayscale);
+ return new DisplayHashParams(mBufferSize, mGrayscaleBuffer);
}
}
@@ -112,16 +112,16 @@
* buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
* HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
* value set here. If {@code null}, the buffer size will not be changed.
- * @param useGrayscale
+ * @param grayscaleBuffer
* Whether the content will be captured in grayscale or color.
* @hide
*/
@DataClass.Generated.Member
public DisplayHashParams(
@Nullable Size bufferSize,
- boolean useGrayscale) {
+ boolean grayscaleBuffer) {
this.mBufferSize = bufferSize;
- this.mUseGrayscale = useGrayscale;
+ this.mGrayscaleBuffer = grayscaleBuffer;
// onConstructed(); // You can define this method to get a callback
}
@@ -141,8 +141,8 @@
* Whether the content will be captured in grayscale or color.
*/
@DataClass.Generated.Member
- public boolean isUseGrayscale() {
- return mUseGrayscale;
+ public boolean isGrayscaleBuffer() {
+ return mGrayscaleBuffer;
}
@Override
@@ -153,7 +153,7 @@
return "DisplayHashParams { " +
"bufferSize = " + mBufferSize + ", " +
- "useGrayscale = " + mUseGrayscale +
+ "grayscaleBuffer = " + mGrayscaleBuffer +
" }";
}
@@ -164,7 +164,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mUseGrayscale) flg |= 0x2;
+ if (mGrayscaleBuffer) flg |= 0x2;
if (mBufferSize != null) flg |= 0x1;
dest.writeByte(flg);
if (mBufferSize != null) dest.writeSize(mBufferSize);
@@ -182,11 +182,11 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean useGrayscale = (flg & 0x2) != 0;
+ boolean grayscaleBuffer = (flg & 0x2) != 0;
Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
this.mBufferSize = bufferSize;
- this.mUseGrayscale = useGrayscale;
+ this.mGrayscaleBuffer = grayscaleBuffer;
// onConstructed(); // You can define this method to get a callback
}
@@ -206,10 +206,10 @@
};
@DataClass.Generated(
- time = 1618436855096L,
+ time = 1619638159453L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
- inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mUseGrayscale\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mUseGrayscale\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java
index d0e4195..c04dd9c 100644
--- a/core/java/android/service/search/SearchUiService.java
+++ b/core/java/android/service/search/SearchUiService.java
@@ -45,6 +45,9 @@
* A service used to share the lifecycle of search UI (open, close, interaction)
* and also to return search result on a query.
*
+ * To understand the lifecycle of search session and how a query get issued,
+ * {@see SearchSession}
+ *
* @hide
*/
@SystemApi
@@ -71,6 +74,10 @@
@Override
public void onCreateSearchSession(SearchContext context, SearchSessionId sessionId) {
mHandler.sendMessage(
+ obtainMessage(SearchUiService::onSearchSessionCreated,
+ SearchUiService.this, context, sessionId));
+ // to be removed
+ mHandler.sendMessage(
obtainMessage(SearchUiService::onCreateSearchSession,
SearchUiService.this, context, sessionId));
}
@@ -119,11 +126,24 @@
/**
* Creates a new search session.
+ *
+ * @deprecated this is method will be removed as soon as
+ * {@link #onSearchSessionCreated(SearchContext, SearchSessionId)}
+ * is adopted by the service.
+ *
+ * @removed
*/
+ @Deprecated
public void onCreateSearchSession(@NonNull SearchContext context,
@NonNull SearchSessionId sessionId) {}
/**
+ * A new search session is created.
+ */
+ public void onSearchSessionCreated(@NonNull SearchContext context,
+ @NonNull SearchSessionId sessionId) {}
+
+ /**
* Called by the client to request search results using a query string.
*/
@MainThread
@@ -132,7 +152,10 @@
@NonNull Consumer<List<SearchTarget>> callback);
/**
- * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s).
+ * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s)
+ * and lifecycle event on the search surface (e.g., visibility change).
+ *
+ * {@see SearchTargetEvent}
*/
@MainThread
public abstract void onNotifyEvent(@NonNull SearchSessionId sessionId,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5a738eb..0958f3f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5191,6 +5191,17 @@
@Override
public void handleMessage(Message msg) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, getMessageName(msg));
+ }
+ try {
+ handleMessageImpl(msg);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
+ private void handleMessageImpl(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index b0d95b3..8dbce1b 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -254,6 +254,11 @@
}
/** @hide */
+ public TranslationContext getTranslationContext() {
+ return mTranslationContext;
+ }
+
+ /** @hide */
public int getTranslatorId() {
return mId;
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 0425572..bdf217d 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -451,13 +451,14 @@
int[] supportedFormats = getSupportedFormatsLocked();
ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+ TranslationCapability capability =
+ getTranslationCapability(translator.getTranslationContext());
mActivity.runOnUiThread(() -> {
// traverse the hierarchy to collect ViewTranslationRequests
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
View rootView = roots.get(rootNum).getView();
- // TODO(b/183589662): call getTranslationCapabilities() for capability
- rootView.dispatchRequestTranslation(viewIds, supportedFormats, /* capability */
- null, requests);
+ rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
+ requests);
}
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
@@ -487,6 +488,15 @@
return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
}
+ private TranslationCapability getTranslationCapability(TranslationContext translationContext) {
+ // We only support text to text capability now, we will query real status from service when
+ // we support more translation capabilities.
+ return new TranslationCapability(TranslationCapability.STATE_ON_DEVICE,
+ translationContext.getSourceSpec(),
+ translationContext.getTargetSpec(), /* uiTranslationEnabled= */ true,
+ /* supportedTranslationFlags= */ 0);
+ }
+
private void findViewsTraversalByAutofillIds(IntArray sourceViewIds) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 166411e..e06d5f0 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -259,6 +259,20 @@
// Used to highlight a word when it is corrected by the IME
private CorrectionHighlighter mCorrectionHighlighter;
+ /**
+ * {@code true} when {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)}
+ * is being executed and {@link InputMethodManager#restartInput(View)} is scheduled to be
+ * called.
+ *
+ * <p>This is also used to avoid an unnecessary invocation of
+ * {@link InputMethodManager#updateSelection(View, int, int, int, int)} when
+ * {@link InputMethodManager#restartInput(View)} is scheduled to be called already
+ * See bug 186582769 for details.</p>
+ *
+ * <p>TODO(186582769): Come up with better way.</p>
+ */
+ private boolean mHasPendingRestartInputForSetText = false;
+
InputContentType mInputContentType;
InputMethodState mInputMethodState;
@@ -1825,6 +1839,29 @@
}
}
+ /**
+ * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+ * schedule {@link InputMethodManager#restartInput(View)}.
+ */
+ void scheduleRestartInputForSetText() {
+ mHasPendingRestartInputForSetText = true;
+ }
+
+ /**
+ * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+ * actually call {@link InputMethodManager#restartInput(View)} if it's scheduled. Does nothing
+ * otherwise.
+ */
+ void maybeFireScheduledRestartInputForSetText() {
+ if (mHasPendingRestartInputForSetText) {
+ final InputMethodManager imm = getInputMethodManager();
+ if (imm != null) {
+ imm.restartInput(mTextView);
+ }
+ mHasPendingRestartInputForSetText = false;
+ }
+ }
+
static final int EXTRACT_NOTHING = -2;
static final int EXTRACT_UNKNOWN = -1;
@@ -1958,7 +1995,8 @@
}
private void sendUpdateSelection() {
- if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
+ if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0
+ && !mHasPendingRestartInputForSetText) {
final InputMethodManager imm = getInputMethodManager();
if (null != imm) {
final int selectionStart = mTextView.getSelectionStart();
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 105c714..b5a5848 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -706,8 +706,7 @@
if (getChildCount() == 0) {
return false;
}
- if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowRight.isFinished()
- || !mEdgeGlowLeft.isFinished())) {
+ if (!mScroller.isFinished()) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 65f3da7..2dd7f02 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -763,8 +763,7 @@
if (getChildCount() == 0) {
return false;
}
- if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowTop.isFinished()
- || !mEdgeGlowBottom.isFinished())) {
+ if (!mScroller.isFinished()) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 07a9a5fa..1a37b59 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6285,11 +6285,10 @@
|| needEditableForNotification) {
createEditorIfNeeded();
mEditor.forgetUndoRedo();
+ mEditor.scheduleRestartInputForSetText();
Editable t = mEditableFactory.newEditable(text);
text = t;
setFilters(t, mFilters);
- InputMethodManager imm = getInputMethodManager();
- if (imm != null) imm.restartInput(this);
} else if (precomputed != null) {
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
@@ -6408,8 +6407,12 @@
notifyListeningManagersAfterTextChanged();
}
- // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
- if (mEditor != null) mEditor.prepareCursorControllers();
+ if (mEditor != null) {
+ // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
+ mEditor.prepareCursorControllers();
+
+ mEditor.maybeFireScheduledRestartInputForSetText();
+ }
}
/**
diff --git a/core/java/com/android/internal/graphics/OWNERS b/core/java/com/android/internal/graphics/OWNERS
new file mode 100644
index 0000000..5851cbb
--- /dev/null
+++ b/core/java/com/android/internal/graphics/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/OWNERS
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 070e6ab..11df5a8 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -43,7 +43,6 @@
void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
- void notifyUserAction(in IVoidResultCallback resultCallback);
- void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible,
- in IVoidResultCallback resultCallback);
+ void notifyUserActionAsync();
+ void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 2fc8d7a..ed1fe1a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -359,26 +359,23 @@
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction(IVoidResultCallback)}
+ * Calls {@link IInputMethodPrivilegedOperations#notifyUserActionAsync()}
*/
@AnyThread
- public void notifyUserAction() {
+ public void notifyUserActionAsync() {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.notifyUserAction(ResultCallbacks.of(value));
- Completable.getResult(value);
+ ops.notifyUserActionAsync();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean,
- * IVoidResultCallback)}.
+ * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibilityAsync(IBinder, boolean)}.
*
* @param showOrHideInputToken placeholder token that maps to window requesting
* {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
@@ -387,15 +384,13 @@
* @param setVisible {@code true} to set IME visible, else hidden.
*/
@AnyThread
- public void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible) {
+ public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.applyImeVisibility(showOrHideInputToken, setVisible, ResultCallbacks.of(value));
- Completable.getResult(value);
+ ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 04528e9..c32951a 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,8 @@
#define ATRACE_TAG ATRACE_TAG_RESOURCES
+#include <mutex>
+
#include "signal.h"
#include "android-base/logging.h"
@@ -26,6 +28,7 @@
#include "utils/misc.h"
#include "utils/Trace.h"
+#include "android_content_res_ApkAssets.h"
#include "android_util_AssetManager_private.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -72,6 +75,19 @@
FORMAT_DIRECTORY = 3,
};
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr) {
+ return *reinterpret_cast<Guarded<std::unique_ptr<const ApkAssets>>*>(ptr);
+}
+
+static jlong CreateGuardedApkAssets(std::unique_ptr<const ApkAssets> assets) {
+ auto guarded_assets = new Guarded<std::unique_ptr<const ApkAssets>>(std::move(assets));
+ return reinterpret_cast<jlong>(guarded_assets);
+}
+
+static void DeleteGuardedApkAssets(Guarded<std::unique_ptr<const ApkAssets>>& apk_assets) {
+ delete &apk_assets;
+}
+
class LoaderAssetsProvider : public AssetsProvider {
public:
static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
@@ -227,7 +243,7 @@
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -279,7 +295,7 @@
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -347,12 +363,12 @@
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
// STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
@@ -383,54 +399,60 @@
}
static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- destroy_info << "{ptr=" << apk_assets;
- if (apk_assets != nullptr) {
- destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
- << ", idmap=" << apk_assets->GetLoadedIdmap()
- << ", {arsc=" << apk_assets->GetLoadedArsc();
- if (auto arsc = apk_assets->GetLoadedArsc()) {
- destroy_info << ", strings=" << arsc->GetStringPool()
- << ", packages=" << &arsc->GetPackages()
- << " [";
- for (auto& package : arsc->GetPackages()) {
- destroy_info << "{unique_ptr=" << &package
- << ", package=" << package.get() << "},";
- }
- destroy_info << "]";
+ {
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ destroy_info << "{ptr=" << apk_assets;
+ if (apk_assets != nullptr) {
+ destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
+ << ", idmap=" << apk_assets->GetLoadedIdmap()
+ << ", {arsc=" << apk_assets->GetLoadedArsc();
+ if (auto arsc = apk_assets->GetLoadedArsc()) {
+ destroy_info << ", strings=" << arsc->GetStringPool()
+ << ", packages=" << &arsc->GetPackages() << " [";
+ for (auto& package : arsc->GetPackages()) {
+ destroy_info << "{unique_ptr=" << &package << ", package=" << package.get()
+ << "},";
+ }
+ destroy_info << "]";
+ }
+ destroy_info << "}";
+ }
+ destroy_info << "}";
}
- destroy_info << "}";
- }
- destroy_info << "}";
- delete apk_assets;
+ DeleteGuardedApkAssets(ApkAssetsFromLong(ptr));
- // Deleting the apk assets did not lead to a crash.
- destroy_info.str("");
- destroy_info.clear();
+ // Deleting the apk assets did not lead to a crash.
+ destroy_info.str("");
+ destroy_info.clear();
}
static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- if (auto path = apk_assets->GetPath()) {
- return env->NewStringUTF(path->data());
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ if (auto path = apk_assets->GetPath()) {
+ return env->NewStringUTF(path->data());
}
return nullptr;
}
static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return env->NewStringUTF(apk_assets->GetDebugName().c_str());
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ return env->NewStringUTF(apk_assets->GetDebugName().c_str());
}
static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
}
static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
}
static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@@ -439,7 +461,8 @@
return 0;
}
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
std::unique_ptr<Asset> asset = apk_assets->GetAssetsProvider()->Open(
path_utf8.c_str(),Asset::AccessMode::ACCESS_RANDOM);
if (asset == nullptr) {
@@ -467,12 +490,13 @@
static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
jstring overlayable_name) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
- const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
- if (packages.empty()) {
- jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
- return 0;
+ const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+ if (packages.empty()) {
+ jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+ return 0;
}
// TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
@@ -502,13 +526,14 @@
}
static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
- const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
- if (packages.empty()) {
- // Must throw to prevent bypass by returning false
- jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
- return 0;
+ const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+ if (packages.empty()) {
+ // Must throw to prevent bypass by returning false
+ jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+ return 0;
}
const auto& overlayable_infos = packages[0]->GetOverlayableMap();
diff --git a/core/jni/android_content_res_ApkAssets.h b/core/jni/android_content_res_ApkAssets.h
new file mode 100644
index 0000000..7e525dc
--- /dev/null
+++ b/core/jni/android_content_res_ApkAssets.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROID_CONTENT_RES_APKASSETS_H
+#define ANDROID_CONTENT_RES_APKASSETS_H
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/MutexGuard.h"
+
+#include "jni.h"
+
+namespace android {
+
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr);
+
+} // namespace android
+
+#endif // ANDROID_CONTENT_RES_APKASSETS_H
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2c69a0..24f9abf 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -42,6 +42,7 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
+#include "android_content_res_ApkAssets.h"
#include "android_util_AssetManager_private.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -50,9 +51,9 @@
#include "nativehelper/ScopedStringChars.h"
#include "nativehelper/ScopedUtfChars.h"
#include "utils/Log.h"
-#include "utils/misc.h"
#include "utils/String8.h"
#include "utils/Trace.h"
+#include "utils/misc.h"
extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
@@ -307,7 +308,9 @@
if (env->ExceptionCheck()) {
return;
}
- apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+
+ auto scoped_assets = ScopedLock(ApkAssetsFromLong(apk_assets_native_ptr));
+ apk_assets.push_back(scoped_assets->get());
}
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ae6dcfd..1bba12f 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -430,6 +430,7 @@
optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto one_handed_mode_activated = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional OneHanded onehanded = 80;
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 9e1692f..e7174cc 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -18,7 +18,7 @@
android:id="@+id/work_widget_mask_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#F3374248"
+ android:background="?android:attr/colorSurfaceVariant"
android:importantForAccessibility="noHideDescendants"
android:clickable="true">
@@ -33,8 +33,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
- android:layout_marginBottom="4dp"
- android:layout_marginRight="4dp"
+ android:layout_marginBottom="12dp"
+ android:layout_marginRight="12dp"
android:src="@drawable/ic_corp_badge_off"
android:clickable="false" />
</FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f097009..d4fa285 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9576,8 +9576,9 @@
<attr name="lStar" format="float"/>
<declare-styleable name="DisplayHashingService">
- <!-- The throttle duration for display hash requests
+ <!-- The interval required between display hash requests. Requests made faster than this
+ delay will be throttled."
@hide @SystemApi -->
- <attr name="throttleDurationMillis" format="integer" />
+ <attr name="durationBetweenRequestsMillis" format="integer" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fbc9678..d3ea52e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4892,4 +4892,7 @@
<bool name="config_supportsMicToggle">false</bool>
<!-- Whether this device is supporting the camera toggle -->
<bool name="config_supportsCamToggle">false</bool>
+
+ <!-- List containing the allowed install sources for accessibility service. -->
+ <string-array name="config_accessibility_allowed_install_source" translatable="false"/>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c716000..0e9526a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3097,7 +3097,7 @@
<public name="playHomeTransitionSound" />
<public name="lStar" />
<!-- @hide @SystemApi -->
- <public name="throttleDurationMillis" />
+ <public name="durationBetweenRequestsMillis" />
<public name="showInInputMethodPicker" />
<public name="effectColor" />
</staging-public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8d29d4..b9f1e20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4357,6 +4357,8 @@
<java-symbol type="drawable" name="ic_accessibility_24dp" />
<java-symbol type="string" name="view_and_control_notification_title" />
<java-symbol type="string" name="view_and_control_notification_content" />
+ <java-symbol type="array" name="config_accessibility_allowed_install_source" />
+
<!-- Translation -->
<java-symbol type="string" name="ui_translation_accessibility_translated_text" />
<java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index b6d408a..eb82c6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,7 +23,6 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
@@ -89,11 +88,11 @@
ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
task1.taskId, task2.taskId, this);
- if ((!task1.isResizeable || !task2.isResizeable)
- && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
+ if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) {
ProtoLog.e(WM_SHELL_TASK_ORG,
- "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
- task1.isResizeable, task2.isResizeable);
+ "Can't pair tasks that doesn't support multi window, "
+ + "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b",
+ task1.supportsMultiWindow, task2.supportsMultiWindow);
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 6719d74..0f5d0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1604,6 +1604,7 @@
// can start fresh.
cancelAllExpandCollapseSwitchAnimations();
}
+ showManageMenu(false /* show */);
// If we're expanded, screenshot the currently expanded bubble (before expanding the newly
// selected bubble) so we can animate it out.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index b9fdaa1..442e7a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -110,6 +110,10 @@
return false;
}
+ if (mDoubleTapDetector.onTouchEvent(event)) {
+ return true;
+ }
+
final int action = event.getAction() & MotionEvent.ACTION_MASK;
final boolean isLandscape = isLandscape();
// Using raw xy to prevent lost track of motion events while moving divider bar.
@@ -136,21 +140,22 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.addMovement(event);
+ releaseTouching();
+
+ if (!mMoving) break;
+
mVelocityTracker.computeCurrentVelocity(1000 /* units */);
final float velocity = isLandscape
? mVelocityTracker.getXVelocity()
: mVelocityTracker.getYVelocity();
- releaseTouching();
- mMoving = false;
-
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
final DividerSnapAlgorithm.SnapTarget snapTarget =
mSplitLayout.findSnapTarget(position, velocity, false /* hardDismiss */);
mSplitLayout.snapToTarget(position, snapTarget);
+ mMoving = false;
break;
}
- mDoubleTapDetector.onTouchEvent(event);
return true;
}
@@ -229,7 +234,7 @@
if (mSplitLayout != null) {
mSplitLayout.onDoubleTappedDivider();
}
- return false;
+ return true;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b64c796..d318a5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -231,6 +231,7 @@
}
private void flingDividePosition(int from, int to) {
+ if (from == to) return;
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
.setDuration(250);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index a18d106..60f7ee2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -49,7 +49,6 @@
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowManager;
@@ -524,9 +523,10 @@
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.addMovement(event);
+ if (!mMoving) break;
+
x = (int) event.getRawX();
y = (int) event.getRawY();
-
mVelocityTracker.computeCurrentVelocity(1000);
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index 27c56fd..d9409ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -30,7 +30,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.os.IBinder;
@@ -92,11 +91,10 @@
// is nothing behind it.
((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
&& triggerTask.parentTaskId == mListener.mPrimary.taskId)
- // if a non-resizable is launched when it is not supported in multi window,
+ // if an activity that is not supported in multi window mode is launched,
// we also need to leave split-screen.
|| ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && !triggerTask.isResizeable
- && !ActivityTaskManager.supportsNonResizableMultiWindow());
+ && !triggerTask.supportsMultiWindow);
// In both cases, dismiss the primary
if (shouldDismiss) {
WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 5a2ef56..1072845 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,7 +46,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.function.BooleanSupplier;
/**
* Proxy to simplify calls into window manager/activity manager
@@ -209,17 +208,10 @@
return false;
}
ActivityManager.RunningTaskInfo topHomeTask = null;
- // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
- // because the value can be changed at runtime.
- final BooleanSupplier supportsNonResizableMultiWindow =
- createSupportsNonResizableMultiWindowSupplier();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
- // Check whether to move resizeable task to split secondary.
- // Also, we have an exception for non-resizable home because we will minimize to show
- // it.
- if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
- && !supportsNonResizableMultiWindow.getAsBoolean()) {
+ // Check whether the task can be moved to split secondary.
+ if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
continue;
}
// Only move fullscreen tasks to split secondary.
@@ -364,21 +356,6 @@
outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
}
- /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
- private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
- return new BooleanSupplier() {
- private Boolean mSupportsNonResizableMultiWindow;
- @Override
- public boolean getAsBoolean() {
- if (mSupportsNonResizableMultiWindow == null) {
- mSupportsNonResizableMultiWindow =
- ActivityTaskManager.supportsNonResizableMultiWindow();
- }
- return mSupportsNonResizableMultiWindow;
- }
- };
- }
-
/**
* Utility to apply a sync transaction serially with other sync transactions.
*
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 134d00b..741773e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -22,6 +22,7 @@
import android.system.helpers.ActivityHelper
import android.util.Log
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -138,7 +139,7 @@
append("$primaryApp $secondaryApp")
}
- @Presubmit
+ @FlakyTest(bugId = 186510496)
@Test
open fun navBarLayerIsAlwaysVisible() {
testSpec.navBarLayerIsAlwaysVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index a7e1d0f..95672f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -65,7 +65,7 @@
@Test
fun pipLayerBecomesVisible() {
testSpec.assertLayers {
- this.isVisible(pipApp.launcherName)
+ this.isVisible(pipApp.windowName)
}
}
@@ -73,9 +73,11 @@
@Test
fun pipWindowBecomesVisible() {
testSpec.assertWm {
- invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
- .then()
- .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+ invoke("pipWindowIsNotVisible") {
+ verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue()
+ }.then().invoke("pipWindowIsVisible") {
+ verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue()
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index b0de029..2b5cd60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -69,6 +69,7 @@
info.configuration.windowConfiguration.setActivityType(mActivityType);
info.token = mToken;
info.isResizeable = true;
+ info.supportsMultiWindow = true;
return info;
}
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 2a70f0d..cb620cc 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -70,9 +70,6 @@
} // namespace
-LoadedPackage::LoadedPackage() = default;
-LoadedPackage::~LoadedPackage() = default;
-
// Precondition: The header passed in has already been verified, so reading any fields and trusting
// the ResChunk_header is safe.
static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 119f531..10666ad 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -94,6 +94,7 @@
};
AssetManager2();
+ explicit AssetManager2(AssetManager2&& other) = default;
// Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
// are not owned by the AssetManager, and must have a longer lifetime.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index d9225cd..3b222c5 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -153,8 +153,6 @@
static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
package_property_t property_flags);
- ~LoadedPackage();
-
// Finds the entry with the specified type name and entry name. The names are in UTF-16 because
// the underlying ResStringPool API expects this. For now this is acceptable, but since
// the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -275,7 +273,7 @@
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
- LoadedPackage();
+ LoadedPackage() = default;
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f4..6fc6d64 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -18,6 +18,7 @@
#define ANDROIDFW_MUTEXGUARD_H
#include <mutex>
+#include <optional>
#include <type_traits>
#include "android-base/macros.h"
@@ -47,34 +48,32 @@
static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
public:
- explicit Guarded() : guarded_() {
+ Guarded() : guarded_(std::in_place, T()) {
}
- template <typename U = T>
- explicit Guarded(const T& guarded,
- typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
- : guarded_(guarded) {
+ explicit Guarded(const T& guarded) : guarded_(std::in_place, guarded) {
}
- template <typename U = T>
- explicit Guarded(T&& guarded,
- typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
- : guarded_(std::move(guarded)) {
+ explicit Guarded(T&& guarded) : guarded_(std::in_place, std::forward<T>(guarded)) {
+ }
+
+ ~Guarded() {
+ std::lock_guard<std::mutex> scoped_lock(lock_);
+ guarded_.reset();
}
private:
friend class ScopedLock<T>;
-
DISALLOW_COPY_AND_ASSIGN(Guarded);
std::mutex lock_;
- T guarded_;
+ std::optional<T> guarded_;
};
template <typename T>
class ScopedLock {
public:
- explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+ explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(*guarded.guarded_) {
}
T& operator*() {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index f5641bd..83a838f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -26,6 +26,7 @@
import androidx.annotation.Nullable;
import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.google.android.material.resources.TextAppearanceConfig;
/**
* A base Activity that has a collapsing toolbar layout is used for the activities intending to
@@ -39,7 +40,8 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+ // Force loading font synchronously for collapsing toolbar layout
+ TextAppearanceConfig.setShouldLoadFontSynchronously(true);
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 0287b1f..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -19,9 +19,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingStart="20dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false">
@@ -29,8 +30,6 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingBottom="16dp"
- android:paddingTop="16dp"
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index fbb84fd..ccbcb89 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -256,4 +256,7 @@
<!-- Default for Settings.Secure.ACCESSIBILITY_BUTTON_MODE -->
<integer name="def_accessibility_button_mode">1</integer>
+ <!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
+ <bool name="def_one_handed_mode_activated">false</bool>
+
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 72fb237..1cfdff8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4872aa4..36f5dba 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -262,6 +262,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2e90d36..941f47f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3401,7 +3401,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 201;
+ private static final int SETTINGS_VERSION = 202;
private final int mUserId;
@@ -4959,6 +4959,22 @@
currentVersion = 201;
}
+ if (currentVersion == 201) {
+ // Version 201: Set the default value for Secure Settings:
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting oneHandedModeActivated = secureSettings.getSettingLocked(
+ Secure.ONE_HANDED_MODE_ACTIVATED);
+ if (oneHandedModeActivated.isNull()) {
+ final boolean defOneHandedModeActivated = getContext().getResources()
+ .getBoolean(R.bool.def_one_handed_mode_activated);
+ secureSettings.insertSettingLocked(
+ Secure.ONE_HANDED_MODE_ACTIVATED,
+ defOneHandedModeActivated ? "1" : "0", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 202;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index c3f1113..666ec27 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -21,5 +21,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:clipChildren="true"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 30e52e9..2cafd1b 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -36,6 +36,8 @@
android:elevation="4dp"
android:importantForAccessibility="no"
android:scrollbars="none"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_weight="1">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
@@ -43,7 +45,9 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:focusable="true"
- android:accessibilityTraversalBefore="@android:id/edit">
+ android:accessibilityTraversalBefore="@android:id/edit"
+ android:clipToPadding="false"
+ android:clipChildren="false">
<include layout="@layout/qs_footer_impl" />
<include layout="@layout/qs_media_divider"
android:id="@+id/divider"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8601bdc..bbb5519 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2727,7 +2727,7 @@
<string name="accessibility_floating_button_action_move_bottom_right">Move bottom right</string>
<!-- Action in accessibility menu to move the accessibility floating button to the edge and hide it to half. [CHAR LIMIT=30]-->
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half">Move to edge and hide</string>
- <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=30]-->
+ <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=36]-->
<string name="accessibility_floating_button_action_move_out_edge_and_show">Move out edge and show</string>
<!-- Device Controls strings -->
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 60b677a..825e008 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -21,12 +21,14 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
import android.icu.text.NumberFormat;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
@@ -35,13 +37,15 @@
import java.util.TimeZone;
/**
- * Controller for an AnimatableClockView.
+ * Controller for an AnimatableClockView. Instantiated by {@link KeyguardClockSwitchController}.
*/
public class AnimatableClockController extends ViewController<AnimatableClockView> {
private static final int FORMAT_NUMBER = 1234567890;
private final StatusBarStateController mStatusBarStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardBypassController mBypassController;
private final int mDozingColor = Color.WHITE;
private int mLockScreenColor;
@@ -59,12 +63,16 @@
AnimatableClockView view,
StatusBarStateController statusBarStateController,
BroadcastDispatcher broadcastDispatcher,
- BatteryController batteryController) {
+ BatteryController batteryController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardBypassController bypassController) {
super(view);
mStatusBarStateController = statusBarStateController;
mIsDozing = mStatusBarStateController.isDozing();
mDozeAmount = mStatusBarStateController.getDozeAmount();
mBroadcastDispatcher = broadcastDispatcher;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mBypassController = bypassController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
mBurmeseLineSpacing = getContext().getResources().getFloat(
@@ -98,14 +106,29 @@
mStatusBarStateController.addCallback(mStatusBarStateListener);
mIsDozing = mStatusBarStateController.isDozing();
mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+
refreshTime();
initColors();
}
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ if (biometricSourceType == BiometricSourceType.FACE
+ && mBypassController.canBypass()) {
+ mView.animateDisappear();
+ }
+ }
+ };
+
@Override
protected void onViewDetached() {
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 0d6f64f..dc14f82 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,6 +27,7 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import java.util.Calendar;
import java.util.TimeZone;
@@ -155,6 +156,21 @@
mLockScreenColor = lockScreenColor;
}
+ void animateDisappear() {
+ if (mTextAnimator == null) {
+ return;
+ }
+
+ setTextStyle(
+ 0 /* weight */,
+ -1 /* text size, no update */,
+ null /* color, no update */,
+ true /* animate */,
+ KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
+ 0 /* delay */,
+ null /* onAnimationEnd */);
+ }
+
void animateCharge(boolean isDozing) {
if (mTextAnimator == null || mTextAnimator.isRunning()) {
// Skip charge animation if dozing animation is already playing.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5559a18..5b83551 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -90,10 +91,11 @@
private SmartspaceSession mSmartspaceSession;
private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
- private int mWallpaperTextColor;
private ConfigurationController mConfigurationController;
private ActivityStarter mActivityStarter;
private FalsingManager mFalsingManager;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardBypassController mBypassController;
/**
* Listener for changes to the color palette.
@@ -147,7 +149,9 @@
ConfigurationController configurationController,
SystemUIFactory systemUIFactory,
ActivityStarter activityStarter,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardBypassController bypassController) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -162,6 +166,8 @@
mSystemUIFactory = systemUIFactory;
mActivityStarter = activityStarter;
mFalsingManager = falsingManager;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mBypassController = bypassController;
}
/**
@@ -185,19 +191,23 @@
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
mClockViewController =
- new AnimatableClockController(
- mView.findViewById(R.id.animatable_clock_view),
- mStatusBarStateController,
- mBroadcastDispatcher,
- mBatteryController);
+ new AnimatableClockController(
+ mView.findViewById(R.id.animatable_clock_view),
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mBatteryController,
+ mKeyguardUpdateMonitor,
+ mBypassController);
mClockViewController.init();
mLargeClockViewController =
- new AnimatableClockController(
- mView.findViewById(R.id.animatable_clock_view_large),
- mStatusBarStateController,
- mBroadcastDispatcher,
- mBatteryController);
+ new AnimatableClockController(
+ mView.findViewById(R.id.animatable_clock_view_large),
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mBatteryController,
+ mKeyguardUpdateMonitor,
+ mBypassController);
mLargeClockViewController.init();
mStatusBarStateController.addCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 2bd7729..9163044 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget
import android.os.SystemProperties
import android.util.Log
@@ -121,6 +122,12 @@
}
// If no recent media, continue with smartspace update
+ if (isMediaRecommendationEmpty(data)) {
+ Log.d(TAG, "Empty media recommendations. Skip showing the card")
+ return
+ }
+
+ // Proceed only if the Smartspace recommendation is not empty.
listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
}
@@ -214,4 +221,10 @@
* Remove a listener that was registered with addListener
*/
fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener)
+
+ /** Check if the Smartspace sends an empty update. */
+ private fun isMediaRecommendationEmpty(data: SmartspaceTarget): Boolean {
+ val mediaRecommendationList: List<SmartspaceAction> = data.getIconGrid()
+ return mediaRecommendationList == null || mediaRecommendationList.isEmpty()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index f7fa5bf..c552e89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -8,7 +8,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
@@ -63,7 +62,6 @@
private int mPageToRestore = -1;
private int mLayoutOrientation;
private int mLayoutDirection;
- private final Rect mClippingRect;
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
private int mExcessHeight;
private int mLastExcessHeight;
@@ -78,14 +76,16 @@
setCurrentItem(0, false);
mLayoutOrientation = getResources().getConfiguration().orientation;
mLayoutDirection = getLayoutDirection();
- mClippingRect = new Rect();
-
- // Make sure there's a space between pages when scroling
- setPageMargin(context.getResources().getDimensionPixelOffset(
- R.dimen.qs_tile_margin_horizontal));
}
private int mLastMaxHeight = -1;
+ @Override
+ public void setPageMargin(int marginPixels) {
+ if (marginPixels != getPageMargin()) {
+ super.setPageMargin(marginPixels);
+ }
+ }
+
public void saveInstanceState(Bundle outState) {
outState.putInt(CURRENT_PAGE, getCurrentItem());
}
@@ -353,14 +353,6 @@
}
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- // Clip to margins
- mClippingRect.set(0, 0, (r - l), b - t);
- setClipBounds(mClippingRect);
- }
-
- @Override
public boolean setMinRows(int minRows) {
mMinRows = minRows;
boolean changed = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 661dbf6..9abb430 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -314,6 +314,9 @@
if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentPadding, mContentPadding);
+ // Set it as double the side margin (to simulate end margin of current page +
+ // start margin of next page).
+ qsPanelController.setPageMargin(2 * mSideMargins);
} else if (view == mHeader) {
// The header contains the QQS panel which needs to have special padding, to
// visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d5cb777..814846c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -480,8 +480,12 @@
private void updateQsBounds() {
if (mLastQSExpansion == 1.0f) {
// Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
- // it's a scrollview and otherwise wouldn't be clipped.
- mQsBounds.set(0, 0, mQSPanelScrollView.getWidth(), mQSPanelScrollView.getHeight());
+ // it's a scrollview and otherwise wouldn't be clipped. However, we set the horizontal
+ // bounds so the pages go to the ends of QSContainerImpl
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mQSPanelScrollView.getLayoutParams();
+ mQsBounds.set(-lp.leftMargin, 0, mQSPanelScrollView.getWidth() + lp.rightMargin,
+ mQSPanelScrollView.getHeight());
}
mQSPanelScrollView.setClipBounds(mQsBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f7b6686..4e16b74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -672,6 +672,16 @@
switchSecurityFooter();
}
+ protected void setPageMargin(int pageMargin) {
+ if (mRegularTileLayout instanceof PagedTileLayout) {
+ ((PagedTileLayout) mRegularTileLayout).setPageMargin(pageMargin);
+ }
+ if (mHorizontalTileLayout != mRegularTileLayout
+ && mHorizontalTileLayout instanceof PagedTileLayout) {
+ ((PagedTileLayout) mHorizontalTileLayout).setPageMargin(pageMargin);
+ }
+ }
+
void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force,
UiEventLogger uiEventLogger) {
if (horizontal != mUsingHorizontalLayout || force) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 93ccfc72..fff3d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -321,5 +321,9 @@
public boolean isExpanded() {
return mView.isExpanded();
}
+
+ void setPageMargin(int pageMargin) {
+ mView.setPageMargin(pageMargin);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index a4bb095..7c0496b 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -16,8 +16,6 @@
package com.android.systemui.sensorprivacy
-import android.app.KeyguardManager
-import android.app.KeyguardManager.KeyguardDismissCallback
import android.content.DialogInterface
import android.content.Intent.EXTRA_PACKAGE_NAME
import android.content.pm.PackageManager
@@ -28,16 +26,15 @@
import android.os.Bundle
import android.os.Handler
import android.text.Html
-import android.util.Log
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.ImageView
import com.android.internal.app.AlertActivity
import com.android.internal.widget.DialogTitle
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
-
+import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
/**
@@ -48,8 +45,8 @@
*/
class SensorUseStartedActivity @Inject constructor(
private val sensorPrivacyController: IndividualSensorPrivacyController,
- private val keyguardManager: KeyguardManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+ private val keyguardStateController: KeyguardStateController,
+ private val keyguardDismissUtil: KeyguardDismissUtil
) : AlertActivity(), DialogInterface.OnClickListener {
companion object {
@@ -180,17 +177,11 @@
override fun onClick(dialog: DialogInterface?, which: Int) {
when (which) {
BUTTON_POSITIVE -> {
- if (keyguardUpdateMonitor.getUserHasTrust(userId)) {
- keyguardManager
- .requestDismissKeyguard(this, object : KeyguardDismissCallback() {
- override fun onDismissError() {
- Log.e(LOG_TAG, "Cannot dismiss keyguard")
- }
-
- override fun onDismissSucceeded() {
- disableSensorPrivacy()
- }
- })
+ if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
+ keyguardDismissUtil.executeWhenUnlocked({
+ disableSensorPrivacy()
+ false
+ }, false)
} else {
disableSensorPrivacy()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 761a1d6..91415f27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -57,7 +57,7 @@
private val windowManager: WindowManager,
private val systemClock: SystemClock
) {
- private var charging: Boolean? = null
+ private var pluggedIn: Boolean? = null
private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled &&
!SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false)
private var normalizedPortPosX: Float = context.resources.getFloat(
@@ -86,18 +86,17 @@
val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
override fun onBatteryLevelChanged(
level: Int,
- pluggedIn: Boolean,
- nowCharging: Boolean
+ nowPluggedIn: Boolean,
+ charging: Boolean
) {
// Suppresses the ripple when it's disabled, or when the state change comes
// from wireless charging.
- if (!rippleEnabled || batteryController.isWirelessCharging) {
+ if (!rippleEnabled || batteryController.isPluggedInWireless) {
return
}
- val wasCharging = charging
- charging = nowCharging
- // Only triggers when the keyguard is active and the device is just plugged in.
- if ((wasCharging == null || !wasCharging) && nowCharging) {
+ val wasPluggedIn = pluggedIn
+ pluggedIn = nowPluggedIn
+ if ((wasPluggedIn == null || !wasPluggedIn) && nowPluggedIn) {
startRippleWithDebounce()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 30d9841..26c6fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -217,7 +217,7 @@
}
companion object {
- const val BYPASS_PANEL_FADE_DURATION = 67
+ const val BYPASS_FADE_DURATION = 67
private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f319022..83a64f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2060,7 +2060,8 @@
int bottom = 0;
int left = 0;
int right = 0;
- boolean visible = qsFraction > 0 || qsPanelBottomY > 0;
+ boolean visible = (qsFraction > 0 || qsPanelBottomY > 0)
+ && !mShouldUseSplitNotificationShade;
int radius = mScrimCornerRadius;
if (visible || !mShouldUseSplitNotificationShade) {
if (!mShouldUseSplitNotificationShade) {
@@ -2079,11 +2080,11 @@
}
}
+ // Fancy clipping for quick settings
+ if (mQs != null) {
+ mQs.setFancyClipping(top, bottom, radius, visible);
+ }
if (!mShouldUseSplitNotificationShade) {
- // Fancy clipping for quick settings
- if (mQs != null) {
- mQs.setFancyClipping(top, bottom, radius, visible);
- }
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
mKeyguardStatusViewController.setClipBounds(visible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 14aeca1..f403cc94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -615,7 +615,7 @@
boolean needsFading = needsBypassFading();
if (needsFading) {
delay = 0;
- fadeoutDuration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION;
+ fadeoutDuration = KeyguardBypassController.BYPASS_FADE_DURATION;
} else if (wakeUnlockPulsing) {
delay = 0;
fadeoutDuration = 240;
@@ -979,7 +979,6 @@
resetAlternateAuth(false);
executeAfterKeyguardGoneAction();
}
-
}
public void showBouncerMessage(String message, ColorStateList colorState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6ae5e90..95a7316 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -43,6 +43,13 @@
boolean isPluggedIn();
/**
+ * Returns {@code true} if the device is currently plugged in via wireless charger.
+ */
+ default boolean isPluggedInWireless() {
+ return false;
+ }
+
+ /**
* Returns {@code true} if the device is currently in power save mode.
*/
boolean isPowerSave();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 288eb3d..9e2c478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -72,6 +72,7 @@
protected int mLevel;
protected boolean mPluggedIn;
+ private boolean mPluggedInWireless;
protected boolean mCharging;
private boolean mStateUnknown = false;
private boolean mCharged;
@@ -175,6 +176,8 @@
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+ mPluggedInWireless = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
+ == BatteryManager.BATTERY_PLUGGED_WIRELESS;
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
@@ -260,6 +263,11 @@
}
@Override
+ public boolean isPluggedInWireless() {
+ return mPluggedInWireless;
+ }
+
+ @Override
public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
// Need to fetch or refresh the estimate, but it may involve binder calls so offload the
// work
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 08cdebd..d2bbcd50d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -34,6 +34,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -59,6 +60,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -489,12 +491,16 @@
? "Dumping..."
: mContext.getString(R.string.heap_dump_tile_name);
if (pmi != null) {
+ final long views = Debug.countInstancesOfClass(View.class);
+ final long enrs = Debug.countInstancesOfClass(ExpandableNotificationRow.class);
+ Log.v(TAG, String.format("updating tile state; rss=%d", pmi.currentRss));
+ Log.v(TAG, String.format("views: %d; ExpandableNotificationRows: %d", views, enrs));
icon.setRss(pmi.currentRss);
state.secondaryLabel =
String.format(
- "rss: %s / %s",
+ "rss=%s views=%d\nenr=%d",
formatBytes(pmi.currentRss * 1024),
- formatBytes(gm.mHeapLimit * 1024));
+ views, enrs);
} else {
icon.setRss(0);
state.secondaryLabel = null;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 39ebe68..5aacac2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -49,6 +49,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -114,6 +115,10 @@
ActivityStarter mActivityStarter;
@Mock
FalsingManager mFalsingManager;
+ @Mock
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ KeyguardBypassController mBypassController;
private KeyguardClockSwitchController mController;
private View mStatusArea;
@@ -152,7 +157,9 @@
mConfigurationController,
mSystemUIFactory,
mActivityStarter,
- mFalsingManager
+ mFalsingManager,
+ mKeyguardUpdateMonitor,
+ mBypassController
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index a9d256b..ac24cde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget
import android.graphics.Color
import androidx.test.filters.SmallTest
@@ -72,6 +73,8 @@
private lateinit var executor: Executor
@Mock
private lateinit var smartspaceData: SmartspaceTarget
+ @Mock
+ private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
private lateinit var mediaDataFilter: MediaDataFilter
private lateinit var dataMain: MediaData
@@ -97,6 +100,7 @@
emptyList(), emptyList(), PACKAGE, null, null, device, true, null)
`when`(smartspaceData.smartspaceTargetId).thenReturn(SMARTSPACE_KEY)
+ `when`(smartspaceData.iconGrid).thenReturn(listOf(smartspaceMediaRecommendationItem))
}
private fun setUser(id: Int) {
@@ -222,7 +226,7 @@
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noMedia_usesSmartspace() {
+ fun testOnSmartspaceMediaDataLoaded_noMedia_nonEmptyRecommendation_usesSmartspace() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
@@ -230,7 +234,18 @@
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noRecentMedia_usesSmartspace() {
+ fun testOnSmartspaceMediaDataLoaded_noMedia_emptyRecommendation_showsNothing() {
+ `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ verify(listener, never())
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ }
+
+ @Test
+ fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
val dataOld = dataMain.copy(active = false, lastActive = 0L)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -240,6 +255,19 @@
}
@Test
+ fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
+ `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+ val dataOld = dataMain.copy(active = false, lastActive = 0L)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ verify(listener, never())
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ }
+
+ @Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 4e404ae..03744b78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -71,8 +71,8 @@
// Verify ripple added to window manager.
captor.value.onBatteryLevelChanged(
0 /* unusedBatteryLevel */,
- false /* plugged in */,
- true /* charging */)
+ true /* plugged in */,
+ false /* charging */)
val attachListenerCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index fd355d8..dc2628f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
@@ -24,7 +26,9 @@
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -33,11 +37,13 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import libcore.util.EmptyArray;
@@ -666,13 +672,66 @@
/**
* Identifies whether the accessibility service is true and designed for accessibility. An
- * accessibility service is considered as accessibility category if
- * {@link AccessibilityServiceInfo#isAccessibilityTool} is true.
+ * accessibility service is considered as accessibility category if meets all conditions below:
+ * <ul>
+ * <li> {@link AccessibilityServiceInfo#isAccessibilityTool} is true</li>
+ * <li> is installed from the trusted install source</li>
+ * </ul>
*
* @param serviceInfo The accessibility service's serviceInfo.
* @return Returns true if it is a true accessibility service.
*/
public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
- return serviceInfo.isAccessibilityTool();
+ if (!serviceInfo.isAccessibilityTool()) {
+ return false;
+ }
+ if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
+ return hasTrustedSystemInstallSource(
+ serviceInfo.getResolveInfo().serviceInfo.packageName);
+ }
+ return true;
+ }
+
+ /** Returns true if the {@code installedPackage} is installed from the trusted install source.
+ */
+ private boolean hasTrustedSystemInstallSource(String installedPackage) {
+ try {
+ InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo(
+ installedPackage);
+ if (installSourceInfo == null) {
+ return false;
+ }
+ final String installSourcePackageName = installSourceInfo.getInitiatingPackageName();
+ if (installSourcePackageName == null || !mPackageManager.getPackageInfo(
+ installSourcePackageName,
+ 0).applicationInfo.isSystemApp()) {
+ return false;
+ }
+ return isTrustedInstallSource(installSourcePackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage);
+ }
+ return false;
+ }
+
+ /** Returns true if the {@code installerPackage} is a trusted install source. */
+ private boolean isTrustedInstallSource(String installerPackage) {
+ final String[] allowedInstallingSources = mContext.getResources().getStringArray(
+ com.android.internal.R.array
+ .config_accessibility_allowed_install_source);
+
+ if (allowedInstallingSources.length == 0) {
+ //Filters unwanted default installers if no allowed install sources.
+ String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService(
+ PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER,
+ mCurrentUserId));
+ return !TextUtils.equals(defaultInstaller, installerPackage);
+ }
+ for (int i = 0; i < allowedInstallingSources.length; i++) {
+ if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index d7f4170..52388ff 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -33,6 +33,8 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
+import android.app.StatsManager;
+import android.app.StatsManager.StatsPullAtomCallback;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -46,6 +48,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -62,10 +65,12 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -177,6 +182,12 @@
NAMESPACE_APP_HIBERNATION,
ActivityThread.currentApplication().getMainExecutor(),
this::onDeviceConfigChanged);
+ getContext().getSystemService(StatsManager.class)
+ .setPullAtomCallback(
+ FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
+ /* metadata */ null, // use default PullAtomMetadata values
+ mBackgroundExecutor,
+ new StatsPullAtomCallbackImpl());
}
}
@@ -272,6 +283,16 @@
} else {
unhibernatePackageForUser(packageName, userId, pkgState);
}
+ final UserLevelState stateSnapshot = new UserLevelState(pkgState);
+ final int userIdSnapshot = userId;
+ mBackgroundExecutor.execute(() -> {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
+ stateSnapshot.packageName,
+ userIdSnapshot,
+ stateSnapshot.hibernated,
+ stateSnapshot.lastUnhibernatedMs);
+ });
List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
}
@@ -910,4 +931,29 @@
com.android.internal.R.bool.config_hibernationDeletesOatArtifactsEnabled);
}
}
+
+ private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+ @Override
+ public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+ if (atomTag != FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS) {
+ return StatsManager.PULL_SKIP;
+ }
+ if (isAppHibernationEnabled()) {
+ List<UserInfo> userInfos = mUserManager.getAliveUsers();
+ final int numUsers = userInfos.size();
+ for (int i = 0; i < numUsers; ++i) {
+ final int userId = userInfos.get(i).id;
+ if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ getHibernatingPackagesForUser(userId).size(),
+ userId)
+ );
+ }
+ }
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
index b75b19d..68c363c 100644
--- a/services/core/java/com/android/server/apphibernation/UserLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -31,6 +31,14 @@
@CurrentTimeMillisLong
public long lastUnhibernatedMs;
+ UserLevelState() {}
+
+ UserLevelState(UserLevelState state) {
+ packageName = state.packageName;
+ hibernated = state.hibernated;
+ lastUnhibernatedMs = state.lastUnhibernatedMs;
+ }
+
@Override
public String toString() {
return "UserLevelState{"
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6282aa6..418b969 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -6117,16 +6117,14 @@
@BinderThread
@Override
- public void notifyUserAction(IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback, () -> mImms.notifyUserAction(mToken));
+ public void notifyUserActionAsync() {
+ mImms.notifyUserAction(mToken);
}
@BinderThread
@Override
- public void applyImeVisibility(IBinder windowToken, boolean setVisible,
- IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.applyImeVisibility(mToken, windowToken, setVisible));
+ public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible) {
+ mImms.applyImeVisibility(mToken, windowToken, setVisible);
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 90694d0..3f2b8ff 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -31,6 +31,7 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.SystemClock;
@@ -224,6 +225,12 @@
}
public boolean serverBasedResumeOnReboot() {
+ // Always use the server based RoR if the HAL isn't installed on device.
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_REBOOT_ESCROW)) {
+ return true;
+ }
+
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false);
}
@@ -374,6 +381,7 @@
try {
escrowKey = getAndClearRebootEscrowKey(kk);
} catch (IOException e) {
+ Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
rebootEscrowUsers);
return;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e7c0a50..431b009 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -382,8 +382,8 @@
// Migrate data usage over a VPN to the TUN network.
for (UnderlyingNetworkInfo info : vpnArray) {
- delta.migrateTun(info.getOwnerUid(), info.getIface(),
- info.getUnderlyingIfaces());
+ delta.migrateTun(info.getOwnerUid(), info.getInterface(),
+ info.getUnderlyingInterfaces());
// Filter out debug entries as that may lead to over counting.
delta.filterDebugEntries();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d6c145..9a3fe82 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4883,6 +4883,11 @@
return super.filterAppAccess(packageName, callingUid, userId);
}
}
+ public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ synchronized (mLock) {
+ super.dump(type, fd, pw, dumpState);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 622b758..3a097a7 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -280,14 +280,15 @@
private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
ArraySet<String>>> datastore) {
+ final int appId = UserHandle.getAppId(uid);
// We make a copy of the per UID state to limit our mutation to one
// operation in the underlying concurrent data structure.
- ArrayMap<String, ArraySet<String>> uidTags = datastore.get(uid);
- if (uidTags != null) {
- uidTags = new ArrayMap<>(uidTags);
+ ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
+ if (appIdTags != null) {
+ appIdTags = new ArrayMap<>(appIdTags);
}
- ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+ ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
if (packageTags != null) {
packageTags = new ArraySet<>(packageTags);
}
@@ -299,17 +300,17 @@
} else {
packageTags = new ArraySet<>(allowListedTags);
}
- if (uidTags == null) {
- uidTags = new ArrayMap<>();
+ if (appIdTags == null) {
+ appIdTags = new ArrayMap<>();
}
- uidTags.put(packageName, packageTags);
- datastore.put(uid, uidTags);
- } else if (uidTags != null) {
- uidTags.remove(packageName);
- if (!uidTags.isEmpty()) {
- datastore.put(uid, uidTags);
+ appIdTags.put(packageName, packageTags);
+ datastore.put(appId, appIdTags);
+ } else if (appIdTags != null) {
+ appIdTags.remove(packageName);
+ if (!appIdTags.isEmpty()) {
+ datastore.put(appId, appIdTags);
} else {
- datastore.remove(uid);
+ datastore.remove(appId);
}
}
}
@@ -318,9 +319,10 @@
@NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
ArraySet<String>>> mappedOps) {
// Only a single lookup from the underlying concurrent data structure
- final ArrayMap<String, ArraySet<String>> uidTags = mappedOps.get(uid);
- if (uidTags != null) {
- final ArraySet<String> packageTags = uidTags.get(packageName);
+ final int appId = UserHandle.getAppId(uid);
+ final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
+ if (appIdTags != null) {
+ final ArraySet<String> packageTags = appIdTags.get(packageName);
if (packageTags != null && packageTags.contains(attributionTag)) {
return true;
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 08a1b7e..65b947c 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1851,7 +1851,7 @@
private long getNextRetryIntervalsMs() {
final int retryDelayIndex = mFailedAttempts - 1;
- final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+ final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis();
// Repeatedly use last item in retry timeout list.
if (retryDelayIndex >= retryIntervalsMs.length) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 65965ad..6198573 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3340,11 +3340,6 @@
}
@Override
- public boolean supportsNonResizableMultiWindow() {
- return mSupportsNonResizableMultiWindow;
- }
-
- @Override
public boolean updateConfiguration(Configuration values) {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 43326df..d5a7619 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1691,7 +1691,11 @@
static boolean isTaskTransitOld(@TransitionOldType int transit) {
return isTaskOpenTransitOld(transit)
- || transit == TRANSIT_OLD_TASK_CLOSE
+ || isTaskCloseTransitOld(transit);
+ }
+
+ static boolean isTaskCloseTransitOld(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_TASK_CLOSE
|| transit == TRANSIT_OLD_TASK_TO_BACK;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e28ab26..a108478 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1538,6 +1538,11 @@
// to cover the activity configuration change.
return false;
}
+ if (r.mStartingData != null && r.mStartingData.hasImeSurface()) {
+ // Currently it is unknown that when will IME window be ready. Reject the case to
+ // avoid flickering by showing IME in inconsistent orientation.
+ return false;
+ }
if (checkOpening) {
if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
// Apply normal rotation animation in case of the activity set different requested
@@ -2108,9 +2113,7 @@
}
// Check if input device can dispatch events to current display.
- // If display type is virtual, will follow the default display.
- if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(),
- displayInfo.type == Display.TYPE_VIRTUAL ? DEFAULT_DISPLAY : mDisplayId)) {
+ if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index af0c3e3..94d81fb 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -129,10 +129,10 @@
private boolean mParsedXml;
/**
- * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
- * than once per throttleTime
+ * Specified duration between requests to generate a display hash in milliseconds. Requests
+ * faster than this delay will be throttled.
*/
- private int mThrottleDurationMillis = 0;
+ private int mDurationBetweenRequestMillis = 0;
/**
* The last time an app requested to generate a display hash in System time.
@@ -203,8 +203,8 @@
return true;
}
- int throttleDurationMs = getThrottleDurationMillis();
- if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+ int mDurationBetweenRequestsMs = getDurationBetweenRequestMillis();
+ if (currentTime - mLastRequestTimeMs < mDurationBetweenRequestsMs) {
return false;
}
@@ -233,7 +233,7 @@
(float) size.getHeight() / boundsInWindow.height());
}
- args.setGrayscale(displayHashParams.isUseGrayscale());
+ args.setGrayscale(displayHashParams.isGrayscaleBuffer());
SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
SurfaceControl.captureLayers(args.build());
@@ -356,11 +356,11 @@
}
}
- private int getThrottleDurationMillis() {
+ private int getDurationBetweenRequestMillis() {
if (!parseXmlProperties()) {
return 0;
}
- return mThrottleDurationMillis;
+ return mDurationBetweenRequestMillis;
}
private boolean parseXmlProperties() {
@@ -406,8 +406,8 @@
}
TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHashingService);
- mThrottleDurationMillis = sa.getInt(
- R.styleable.DisplayHashingService_throttleDurationMillis, 0);
+ mDurationBetweenRequestMillis = sa.getInt(
+ R.styleable.DisplayHashingService_durationBetweenRequestsMillis, 0);
sa.recycle();
mParsedXml = true;
return true;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 84616c0..aa7e6c9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -116,10 +116,8 @@
/** Notifies that the input device configuration has changed. */
@Override
public void notifyConfigurationChanged() {
- // TODO(multi-display): Notify proper displays that are associated with this input device.
-
synchronized (mService.mGlobalLock) {
- mService.getDefaultDisplayContentLocked().sendNewConfiguration();
+ mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
}
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d9c5fa4..dc07988 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2657,9 +2657,12 @@
}
void addStartingWindowsForVisibleActivities() {
+ final ArrayList<Task> addedTasks = new ArrayList<>();
forAllActivities((r) -> {
- if (r.mVisibleRequested) {
+ final Task task = r.getTask();
+ if (r.mVisibleRequested && r.mStartingData == null && !addedTasks.contains(task)) {
r.showStartingWindow(true /*taskSwitch*/);
+ addedTasks.add(task);
}
});
}
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 2124ed6..66ae0eb 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -39,4 +39,9 @@
return mService.mStartingSurfaceController.createTaskSnapshotSurface(activity,
mSnapshot);
}
+
+ @Override
+ boolean hasImeSurface() {
+ return mSnapshot.hasImeSurface();
+ }
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index a5bd797..59de43a 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -40,4 +40,9 @@
* {@link StartingSurface#remove}
*/
abstract StartingSurface createStartingSurface(ActivityRecord activity);
+
+ /** @see android.window.TaskSnapshot#hasImeSurface() */
+ boolean hasImeSurface() {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a9b06ca..c3815c1 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -118,7 +118,9 @@
return null;
}
if (topFullscreenActivity.getWindowConfiguration().getRotation()
- != taskSnapshot.getRotation()) {
+ != taskSnapshot.getRotation()
+ // Use normal rotation to avoid flickering of IME window in old orientation.
+ && !taskSnapshot.hasImeSurface()) {
// The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
// that the activity will be updated to the same rotation as the snapshot. Since
// the transition is not started yet, fixed rotation transform needs to be applied
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2a0041a..9fad7da 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4079,6 +4079,7 @@
info.lastActiveTime = lastActiveTime;
info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
+ info.supportsMultiWindow = supportsMultiWindow();
info.configuration.setTo(getConfiguration());
// Update to the task's current activity type and windowing mode which may differ from the
// window configuration
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index cda8c4b..87f685e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1274,7 +1274,15 @@
for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
- return mLaunchRootTasks.get(i).task;
+ final Task launchRootTask = mLaunchRootTasks.get(i).task;
+ // Return the focusable root task for improving the UX with staged split screen.
+ final Task adjacentRootTask = launchRootTask != null
+ ? launchRootTask.mAdjacentTask : null;
+ if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
+ return adjacentRootTask;
+ } else {
+ return launchRootTask;
+ }
}
}
return null;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bea733b..b1c7e19 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -37,6 +37,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -2696,7 +2697,14 @@
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
- mDisplayContent.showImeScreenshot();
+ final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
+ final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
+ && imeTarget.getWindow().getTask() == task;
+ // Attach and show the IME screenshot when the task is the IME target and performing
+ // task closing transition to the next task.
+ if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
+ mDisplayContent.showImeScreenshot();
+ }
}
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index d076434..51bc99a 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -27,3 +27,4 @@
per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2a0feaf..24f73c3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -883,6 +883,12 @@
synchronized (getLockObject()) {
// Check whether the user is affiliated, *before* removing its data.
boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
+ if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+ // Disable network and security logging
+ mInjector.securityLogSetLoggingEnabledProperty(false);
+ mSecurityLogMonitor.stop();
+ setNetworkLoggingActiveInternal(false);
+ }
removeUserData(userHandle);
if (!isRemovedUserAffiliated) {
// We discard the logs when unaffiliated users are deleted (so that the
@@ -8552,11 +8558,12 @@
synchronized (getLockObject()) {
enforceCanSetProfileOwnerLocked(
caller, who, userHandle, hasIncompatibleAccountsOrNonAdb);
- Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle),
- "Component " + who + " not installed for userId:" + userHandle);
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
- Preconditions.checkArgument(admin != null && !getUserData(
- userHandle).mRemovingAdmins.contains(who), "Not active admin: " + who);
+ Preconditions.checkArgument(
+ isPackageInstalledForUser(who.getPackageName(), userHandle)
+ && admin != null
+ && !getUserData(userHandle).mRemovingAdmins.contains(who),
+ "Not active admin: " + who);
final int parentUserId = getProfileParentId(userHandle);
// When trying to set a profile owner on a new user, it may be that this user is
@@ -8896,17 +8903,17 @@
}
@Override
- public ComponentName getProfileOwnerAsUser(int userHandle) {
+ public ComponentName getProfileOwnerAsUser(int userId) {
if (!mHasFeature) {
return null;
}
- Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userHandle));
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
synchronized (getLockObject()) {
- return mOwners.getProfileOwnerComponent(userHandle);
+ return mOwners.getProfileOwnerComponent(userId);
}
}
@@ -9350,19 +9357,20 @@
public List<UserHandle> listForegroundAffiliatedUsers() {
checkIsDeviceOwner(getCallerIdentity());
- int userId = mInjector.binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId());
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ int userId = getCurrentForegroundUserId();
+ boolean isAffiliated;
+ synchronized (getLockObject()) {
+ isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
+ }
- boolean isAffiliated;
- synchronized (getLockObject()) {
- isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
- }
+ if (!isAffiliated) return Collections.emptyList();
- if (!isAffiliated) return Collections.emptyList();
+ List<UserHandle> users = new ArrayList<>(1);
+ users.add(UserHandle.of(userId));
- List<UserHandle> users = new ArrayList<>(1);
- users.add(UserHandle.of(userId));
-
- return users;
+ return users;
+ });
}
protected int getProfileParentId(int userHandle) {
@@ -13319,12 +13327,10 @@
final CallerIdentity caller = getCallerIdentity();
final long ident = mInjector.binderClearCallingIdentity();
try {
- final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser(
- packageName, caller.getUserId());
- Preconditions.checkArgument(caller.getUid() == uidForPackage,
+ final List<String> callerUidPackageNames = Arrays.asList(
+ mInjector.getPackageManager().getPackagesForUid(caller.getUid()));
+ Preconditions.checkArgument(callerUidPackageNames.contains(packageName),
"Caller uid doesn't match the one for the provided package.");
- } catch (NameNotFoundException e) {
- throw new IllegalArgumentException("Invalid package provided " + packageName, e);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -14124,7 +14130,7 @@
}
}
- private boolean isUserAffiliatedWithDeviceLocked(int userId) {
+ private boolean isUserAffiliatedWithDeviceLocked(@UserIdInt int userId) {
if (!mOwners.hasDeviceOwner()) {
return false;
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 75432cd..9706d7f 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -29,6 +29,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
import android.os.UserHandle;
@@ -79,7 +80,7 @@
*/
public static boolean enabled() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, "enabled",
- false);
+ false) || SystemProperties.getBoolean("persist.profcollectd.enabled_override", false);
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 3bc1c8a..64dad7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -80,6 +80,7 @@
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
@@ -771,10 +772,10 @@
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6);
assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
- mService.removeLocked(pi6, null);
+ mService.removeLocked(pi6, null, REMOVE_REASON_UNDEFINED);
assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
- mService.removeLocked(pi8, null);
+ mService.removeLocked(pi8, null, REMOVE_REASON_UNDEFINED);
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
}
@@ -1252,7 +1253,8 @@
setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
}
assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- mService.removeLocked(TEST_CALLING_UID);
+ mService.removeLocked(TEST_CALLING_UID,
+ REMOVE_REASON_UNDEFINED);
assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
}
@@ -1292,7 +1294,7 @@
}
assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
for (int i = 0; i < numAlarms; i++) {
- mService.removeLocked(pis[i], null);
+ mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
}
}
@@ -1422,7 +1424,7 @@
mTestTimer.expire();
assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
for (int i = 0; i < numAlarms; i++) {
- mService.removeLocked(pis[i], null);
+ mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
}
}
@@ -1492,13 +1494,13 @@
assertEquals(trigger6, mTestTimer.getElapsed());
assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
- mService.removeLocked(wakeFromIdle6, null);
+ mService.removeLocked(wakeFromIdle6, null, REMOVE_REASON_UNDEFINED);
assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
assertEquals(trigger10, mTestTimer.getElapsed());
assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());
- mService.removeLocked(wakeFromIdle10, null);
+ mService.removeLocked(wakeFromIdle10, null, REMOVE_REASON_UNDEFINED);
assertNull(mService.mNextWakeFromIdle);
}
@@ -1524,13 +1526,13 @@
setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
- mService.removeLocked(wakeFromIdle5, null);
+ mService.removeLocked(wakeFromIdle5, null, REMOVE_REASON_UNDEFINED);
assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());
- mService.removeLocked(wakeFromIdle8, null);
+ mService.removeLocked(wakeFromIdle8, null, REMOVE_REASON_UNDEFINED);
assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
- mService.removeLocked(idleUntilPi, null);
+ mService.removeLocked(idleUntilPi, null, REMOVE_REASON_UNDEFINED);
assertNull(mService.mPendingIdleUntil);
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
@@ -1641,7 +1643,7 @@
verify(pis[0], never()).send(eq(mMockContext), eq(0), any(Intent.class), any(),
any(Handler.class), isNull(), any());
- mService.removeLocked(idleUntil, null);
+ mService.removeLocked(idleUntil, null, REMOVE_REASON_UNDEFINED);
mTestTimer.expire();
// Now, the first 5 alarms (upto i = 4) should expire.
for (int i = 0; i < 5; i++) {
@@ -1688,14 +1690,16 @@
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// Refresh the state
- mService.removeLocked(TEST_CALLING_UID);
+ mService.removeLocked(TEST_CALLING_UID,
+ REMOVE_REASON_UNDEFINED);
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// Refresh the state
- mService.removeLocked(TEST_CALLING_UID);
+ mService.removeLocked(TEST_CALLING_UID,
+ REMOVE_REASON_UNDEFINED);
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 45f43e8..ee00cb2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -43,9 +43,12 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,8 +84,10 @@
private static final int APP_PID = 2000;
private static final int SYSTEM_PID = 558;
private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+ private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility";
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
- "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
+ TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest");
+ private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package";
private static final int[] ALWAYS_DISPATCH_EVENTS = {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -142,19 +147,40 @@
@Mock
private AccessibilityServiceInfo mMockA11yServiceInfo;
@Mock
+ private ResolveInfo mMockResolveInfo;
+ @Mock
+ private ServiceInfo mMockServiceInfo;
+ @Mock
+ private ApplicationInfo mMockApplicationInfo;
+ @Mock
+ private ApplicationInfo mMockSourceApplicationInfo;
+ @Mock
+ private PackageInfo mMockSourcePackageInfo;
+ @Mock
private PolicyWarningUIController mPolicyWarningUIController;
@Before
- public void setUp() {
+ public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext.setMockPackageManager(mMockPackageManager);
mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
mContext.getOrCreateTestableResources().addOverride(
R.dimen.accessibility_focus_highlight_stroke_width, 1);
+ mContext.getOrCreateTestableResources().addOverride(R.array
+ .config_accessibility_allowed_install_source,
+ new String[]{ALLOWED_INSTALL_PACKAGE_NAME});
+ when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+ when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn(
+ mMockSourcePackageInfo);
+
+ mMockResolveInfo.serviceInfo = mMockServiceInfo;
+ mMockServiceInfo.applicationInfo = mMockApplicationInfo;
+ mMockServiceInfo.packageName = TEST_PACKAGE_NAME;
+ mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
mPolicyWarningUIController, mContext, mMockA11yUserManager);
@@ -595,28 +621,7 @@
}
@Test
- public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() {
- final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
- boundServices.add(mMockA11yServiceConnection);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
- }
-
- @Test
- public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() {
- onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction();
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
- any());
- }
-
- @Test
- public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() {
+ public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
boundServices.add(mMockA11yServiceConnection);
when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
@@ -628,8 +633,8 @@
}
@Test
- public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+ public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() {
+ onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
@@ -638,8 +643,65 @@
}
@Test
+ public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(true);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() {
+ onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction();
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+ any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction()
+ throws PackageManager.NameNotFoundException {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(false);
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
+ ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null,
+ ALLOWED_INSTALL_PACKAGE_NAME);
+ when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+ installSourceInfo);
+ when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction()
+ throws PackageManager.NameNotFoundException {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null);
+ when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+ installSourceInfo);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
+ }
+
+ @Test
public void onSwitchUser_differentUser_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+ onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 81570a1..fe0df58 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -253,6 +253,8 @@
doReturn(new String[] {admin.getPackageName()}).when(mServices.ipackageManager)
.getPackagesForUid(eq(packageUid));
+ doReturn(new String[] {admin.getPackageName()}).when(mServices.packageManager)
+ .getPackagesForUid(eq(packageUid));
// Set up getPackageInfo().
markPackageAsInstalled(admin.getPackageName(), ai, UserHandle.getUserId(packageUid));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e1eef76..e09606e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -54,6 +54,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1909,6 +1912,32 @@
verify(t).show(mDisplayContent.mImeScreenshot);
}
+ @UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
+ @Test
+ public void testShowImeScreenshot() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+ doReturn(true).when(task).okToAnimate();
+ ArrayList<WindowContainer> sources = new ArrayList<>();
+ sources.add(activity);
+
+ mDisplayContent.setImeLayeringTarget(win);
+ spyOn(mDisplayContent);
+
+ // Expecting the IME screenshot only be attached when performing task closing transition.
+ task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+ false /* isVoiceInteraction */, sources);
+ verify(mDisplayContent).showImeScreenshot();
+
+ clearInvocations(mDisplayContent);
+ activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
+ false /* isVoiceInteraction */, sources);
+ verify(mDisplayContent, never()).showImeScreenshot();
+ }
+
@Test
public void testRotateBounds_keepSamePhysicalPosition() {
final DisplayContent dc =
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 9289ce4..67b273a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -101,6 +101,29 @@
}
@Test
+ public void getLaunchRootTask_checksFocusedRootTask() {
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task rootTask = createTaskWithActivity(
+ taskDisplayArea,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, ON_TOP, true);
+ rootTask.mCreatedByOrganizer = true;
+
+ final Task adjacentRootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ adjacentRootTask.mCreatedByOrganizer = true;
+ adjacentRootTask.mAdjacentTask = rootTask;
+ rootTask.mAdjacentTask = adjacentRootTask;
+
+ taskDisplayArea.setLaunchRootTask(rootTask,
+ new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+ Task actualRootTask = taskDisplayArea.getLaunchRootTask(
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, null /* options */,
+ null /* sourceTask */, 0 /*launchFlags*/);
+ assertTrue(actualRootTask.isFocusedRootTaskOnDisplay());
+ }
+
+ @Test
public void getLaunchRootTask_fromLaunchAdjacentFlagRoot_checksAdjacentRoot() {
final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
final Task rootTask = createTask(
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index d8bd6a5..c5fc436 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -3448,4 +3448,13 @@
public Handler getHandler() {
return mHandler;
}
+
+ /**
+ * Sets this {@link ConnectionService} ready for testing purposes.
+ * @hide
+ */
+ @VisibleForTesting
+ public void setReadyForTest() {
+ mAreAccountsInitialized = true;
+ }
}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 3b28616..8df41fb 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -183,16 +183,6 @@
}
/**
- * @return the absolute radio frequency channel number for this physical channel,
- * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
- * @deprecated Use {@link #getDownlinkChannelNumber()} to get the channel number.
- */
- @Deprecated
- public int getChannelNumber() {
- return getDownlinkChannelNumber();
- }
-
- /**
* @return the rough frequency range for this physical channel,
* {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown.
* @see {@link ServiceState#FREQUENCY_RANGE_LOW}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 05f5d29..179aead 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15036,6 +15036,15 @@
"CAPABILITY_SLICING_CONFIG_SUPPORTED";
/**
+ * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on
+ * respective methods for more information.
+ *
+ * @hide
+ */
+ public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED =
+ "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
+
+ /**
* A list of the radio interface capability values with public valid constants.
*
* Here is a related list for the systemapi-only valid constants:
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
index 87cfb34..f23ba26 100644
--- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
+++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -36,15 +36,15 @@
@Test
fun testParcelUnparcel() {
val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
- assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
- assertEquals(TEST_IFACE, testInfo.iface)
- assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+ assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid())
+ assertEquals(TEST_IFACE, testInfo.getInterface())
+ assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces())
assertParcelSane(testInfo, 3)
val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
- assertEquals(0, emptyInfo.ownerUid)
- assertEquals(String(), emptyInfo.iface)
- assertEquals(listOf(), emptyInfo.underlyingIfaces)
+ assertEquals(0, emptyInfo.getOwnerUid())
+ assertEquals(String(), emptyInfo.getInterface())
+ assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces())
assertParcelSane(emptyInfo, 3)
}
}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d966f70..f277e94 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5898,9 +5898,9 @@
assertEquals("Should have exactly one VPN:", 1, infos.length);
UnderlyingNetworkInfo info = infos[0];
assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid());
- assertEquals("Unexpected VPN interface:", vpnIfname, info.getIface());
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface());
assertSameElementsNoDuplicates(underlyingIfaces,
- info.getUnderlyingIfaces().toArray(new String[0]));
+ info.getUnderlyingInterfaces().toArray(new String[0]));
} else {
assertEquals(0, infos.length);
return;
@@ -6044,8 +6044,8 @@
// network for the VPN...
verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
any(List.class), any() /* anyString() doesn't match null */,
- argThat(infos -> infos.get(0).getUnderlyingIfaces().size() == 1
- && WIFI_IFNAME.equals(infos.get(0).getUnderlyingIfaces().get(0))));
+ argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1
+ && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0))));
verifyNoMoreInteractions(mStatsManager);
reset(mStatsManager);
@@ -6059,8 +6059,8 @@
waitForIdle();
verify(mStatsManager).notifyNetworkStatus(any(List.class),
any(List.class), any() /* anyString() doesn't match null */,
- argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingIfaces().size() == 1
- && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingIfaces().get(0))));
+ argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1
+ && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0))));
mEthernetNetworkAgent.disconnect();
waitForIdle();
reset(mStatsManager);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 7cfd275..9410886 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -82,7 +82,7 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setRetryIntervalsMs(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
@@ -134,7 +134,7 @@
@Test
public void testBuilderRequiresNonNullRetryInterval() {
try {
- newBuilder().setRetryIntervalsMs(null);
+ newBuilder().setRetryIntervalsMillis(null);
fail("Expected exception due to invalid retryIntervalMs");
} catch (IllegalArgumentException e) {
}
@@ -143,7 +143,7 @@
@Test
public void testBuilderRequiresNonEmptyRetryInterval() {
try {
- newBuilder().setRetryIntervalsMs(new long[0]);
+ newBuilder().setRetryIntervalsMillis(new long[0]);
fail("Expected exception due to invalid retryIntervalMs");
} catch (IllegalArgumentException e) {
}
@@ -174,7 +174,7 @@
assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
- assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+ assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 044bef5..a88f112 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -38,7 +38,7 @@
public void setUp() throws Exception {
super.setUp();
- mFirstRetryInterval = mConfig.getRetryIntervalsMs()[0];
+ mFirstRetryInterval = mConfig.getRetryIntervalsMillis()[0];
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b78f48c..6364ccd 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -78,6 +78,8 @@
return "interpolator";
case ResourceType::kLayout:
return "layout";
+ case ResourceType::kMacro:
+ return "macro";
case ResourceType::kMenu:
return "menu";
case ResourceType::kMipmap:
@@ -119,6 +121,7 @@
{"integer", ResourceType::kInteger},
{"interpolator", ResourceType::kInterpolator},
{"layout", ResourceType::kLayout},
+ {"macro", ResourceType::kMacro},
{"menu", ResourceType::kMenu},
{"mipmap", ResourceType::kMipmap},
{"navigation", ResourceType::kNavigation},
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cf93870..307c21d 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -57,6 +57,7 @@
kInteger,
kInterpolator,
kLayout,
+ kMacro,
kMenu,
kMipmap,
kNavigation,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 24c60b7..1efabbb 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -627,6 +627,16 @@
}
return true;
+ } else if (resource_type == "macro") {
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name() << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.type = ResourceType::kMacro;
+ out_resource->name.entry = maybe_name.value().to_string();
+ return ParseMacro(parser, out_resource);
}
if (can_be_item) {
@@ -726,16 +736,8 @@
return true;
}
-/**
- * Reads the entire XML subtree and attempts to parse it as some Item,
- * with typeMask denoting which items it can be. If allowRawValue is
- * true, a RawString is returned if the XML couldn't be parsed as
- * an Item. If allowRawValue is false, nullptr is returned in this
- * case.
- */
-std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
- const uint32_t type_mask,
- const bool allow_raw_value) {
+std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree(
+ xml::XmlPullParser* parser) {
const size_t begin_xml_line = parser->line_number();
std::string raw_value;
@@ -745,30 +747,60 @@
return {};
}
- if (!style_string.spans.empty()) {
+ return FlattenedXmlSubTree{.raw_value = raw_value,
+ .style_string = style_string,
+ .untranslatable_sections = untranslatable_sections,
+ .namespace_resolver = parser,
+ .source = source_.WithLine(begin_xml_line)};
+}
+
+/**
+ * Reads the entire XML subtree and attempts to parse it as some Item,
+ * with typeMask denoting which items it can be. If allowRawValue is
+ * true, a RawString is returned if the XML couldn't be parsed as
+ * an Item. If allowRawValue is false, nullptr is returned in this
+ * case.
+ */
+std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
+ const bool allow_raw_value) {
+ auto sub_tree = CreateFlattenSubTree(parser);
+ if (!sub_tree.has_value()) {
+ return {};
+ }
+ return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_);
+}
+
+std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree,
+ const uint32_t type_mask, const bool allow_raw_value,
+ ResourceTable& table,
+ const android::ConfigDescription& config,
+ IDiagnostics& diag) {
+ if (!xmlsub_tree.style_string.spans.empty()) {
// This can only be a StyledString.
std::unique_ptr<StyledString> styled_string =
- util::make_unique<StyledString>(table_->string_pool.MakeRef(
- style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
- styled_string->untranslatable_sections = std::move(untranslatable_sections);
+ util::make_unique<StyledString>(table.string_pool.MakeRef(
+ xmlsub_tree.style_string,
+ StringPool::Context(StringPool::Context::kNormalPriority, config)));
+ styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
return std::move(styled_string);
}
auto on_create_reference = [&](const ResourceName& name) {
// name.package can be empty here, as it will assume the package name of the
// table.
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->SetSource(source_.WithLine(begin_xml_line));
- table_->AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), diag_);
+ auto id = util::make_unique<Id>();
+ id->SetSource(xmlsub_tree.source);
+ return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag);
};
// Process the raw value.
- std::unique_ptr<Item> processed_item =
- ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
+ std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute(
+ xmlsub_tree.raw_value, type_mask, on_create_reference);
if (processed_item) {
// Fix up the reference.
- if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
- ResolvePackage(parser, ref);
+ if (auto ref = ValueCast<Reference>(processed_item.get())) {
+ ref->allow_raw = allow_raw_value;
+ ResolvePackage(xmlsub_tree.namespace_resolver, ref);
}
return processed_item;
}
@@ -777,17 +809,16 @@
if (type_mask & android::ResTable_map::TYPE_STRING) {
// Use the trimmed, escaped string.
std::unique_ptr<String> string = util::make_unique<String>(
- table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
- string->untranslatable_sections = std::move(untranslatable_sections);
+ table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config)));
+ string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
return std::move(string);
}
if (allow_raw_value) {
// We can't parse this so return a RawString if we are allowed.
- return util::make_unique<RawString>(
- table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
- StringPool::Context(config_)));
- } else if (util::TrimWhitespace(raw_value).empty()) {
+ return util::make_unique<RawString>(table.string_pool.MakeRef(
+ util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config)));
+ } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) {
// If the text is empty, and the value is not allowed to be a string, encode it as a @null.
return ResourceUtils::MakeNull();
}
@@ -850,6 +881,35 @@
return true;
}
+bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ auto sub_tree = CreateFlattenSubTree(parser);
+ if (!sub_tree) {
+ return false;
+ }
+
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<macro> tags cannot be declared in configurations other than the default "
+ "configuration'");
+ return false;
+ }
+
+ auto macro = std::make_unique<Macro>();
+ macro->raw_value = std::move(sub_tree->raw_value);
+ macro->style_string = std::move(sub_tree->style_string);
+ macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections);
+
+ for (const auto& decl : parser->package_decls()) {
+ macro->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = decl.prefix,
+ .package_name = decl.package.package,
+ .is_private = decl.package.private_namespace});
+ }
+
+ out_resource->value = std::move(macro);
+ return true;
+}
+
bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (options_.visibility) {
diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index af0db8c..5c92def 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,14 @@
Maybe<Visibility::Level> visibility;
};
+struct FlattenedXmlSubTree {
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+ xml::IPackageDeclStack* namespace_resolver;
+ Source source;
+};
+
/*
* Parses an XML file for resources and adds them to a ResourceTable.
*/
@@ -67,9 +75,16 @@
const ResourceParserOptions& options = {});
bool Parse(xml::XmlPullParser* parser);
+ static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask,
+ bool allow_raw_value, ResourceTable& table,
+ const android::ConfigDescription& config,
+ IDiagnostics& diag);
+
private:
DISALLOW_COPY_AND_ASSIGN(ResourceParser);
+ std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
+
// Parses the XML subtree as a StyleString (flattened XML representation for strings with
// formatting). If parsing fails, false is returned and the out parameters are left in an
// unspecified state. Otherwise,
@@ -96,7 +111,7 @@
bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format);
bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
-
+ bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
@@ -108,8 +123,7 @@
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
const android::StringPiece& tag);
- bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
- ParsedResource* out_resource);
+ bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4a509be..279ebcba 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -336,6 +336,90 @@
EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
}
+TEST_F(ResourceParserTest, ParseMacro) {
+ std::string input = R"(<macro name="foo">12345</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("12345"));
+ EXPECT_THAT(macro->style_string.str, Eq("12345"));
+ EXPECT_THAT(macro->style_string.spans, IsEmpty());
+ EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+ EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) {
+ std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house."));
+ EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house."));
+ EXPECT_THAT(macro->style_string.spans.size(), Eq(1));
+ EXPECT_THAT(macro->style_string.spans[0].name, Eq("b"));
+ EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12));
+ EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16));
+ ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1));
+ EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12));
+ EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17));
+ EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroNamespaces) {
+ std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android">
+@app:string/foo</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo"));
+ EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo"));
+ EXPECT_THAT(macro->style_string.spans, IsEmpty());
+ EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+ EXPECT_THAT(macro->alias_namespaces.size(), Eq(1));
+ EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app"));
+ EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android"));
+ EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false));
+}
+
+TEST_F(ResourceParserTest, ParseMacroReference) {
+ std::string input = R"(<string name="res_string">@macro/foo</string>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Reference* macro = test::GetValue<Reference>(&table_, "string/res_string");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING));
+ EXPECT_THAT(macro->allow_raw, Eq(false));
+
+ input = R"(<style name="foo">
+ <item name="bar">@macro/foo</item>
+ </style>)";
+
+ ASSERT_TRUE(TestParse(input));
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_THAT(style, NotNull());
+ EXPECT_THAT(style->entries.size(), Eq(1));
+
+ macro = ValueCast<Reference>(style->entries[0].value.get());
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->type_flags, Eq(0U));
+ EXPECT_THAT(macro->allow_raw, Eq(true));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNoNameFail) {
+ std::string input = R"(<macro>12345</macro>)";
+ ASSERT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) {
+ const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
+ std::string input = R"(<macro name="foo">12345</macro>)";
+ ASSERT_FALSE(TestParse(input, watch_config));
+}
+
// Old AAPT allowed attributes to be defined under different configurations, but ultimately
// stored them with the default configuration. Check that we have the same behavior.
TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 5b43df6..e0e80ac 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -628,7 +628,7 @@
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& value, uint32_t type_mask,
- const std::function<void(const ResourceName&)>& on_create_reference) {
+ const std::function<bool(const ResourceName&)>& on_create_reference) {
using android::ResTable_map;
auto null_or_empty = TryParseNullOrEmpty(value);
@@ -639,8 +639,11 @@
bool create = false;
auto reference = TryParseReference(value, &create);
if (reference) {
+ reference->type_flags = type_mask;
if (create && on_create_reference) {
- on_create_reference(reference->name.value());
+ if (!on_create_reference(reference->name.value())) {
+ return {};
+ }
}
return std::move(reference);
}
@@ -689,7 +692,7 @@
*/
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& str, const Attribute* attr,
- const std::function<void(const ResourceName&)>& on_create_reference) {
+ const std::function<bool(const ResourceName&)>& on_create_reference) {
using android::ResTable_map;
const uint32_t type_mask = attr->type_mask;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f77766e..be493db 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -204,11 +204,11 @@
*/
std::unique_ptr<Item> TryParseItemForAttribute(
const android::StringPiece& value, const Attribute* attr,
- const std::function<void(const ResourceName&)>& on_create_reference = {});
+ const std::function<bool(const ResourceName&)>& on_create_reference = {});
std::unique_ptr<Item> TryParseItemForAttribute(
const android::StringPiece& value, uint32_t type_mask,
- const std::function<void(const ResourceName&)>& on_create_reference = {});
+ const std::function<bool(const ResourceName&)>& on_create_reference = {});
uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 574bd2e..2a90f26 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -111,12 +111,15 @@
if (!other) {
return false;
}
- return reference_type == other->reference_type &&
- private_reference == other->private_reference && id == other->id &&
- name == other->name;
+ return reference_type == other->reference_type && private_reference == other->private_reference &&
+ id == other->id && name == other->name && type_flags == other->type_flags;
}
bool Reference::Flatten(android::Res_value* out_value) const {
+ if (name && name.value().type == ResourceType::kMacro) {
+ return false;
+ }
+
const ResourceId resid = id.value_or_default(ResourceId(0));
const bool dynamic = resid.is_valid() && is_dynamic;
@@ -551,7 +554,7 @@
return this_type_mask == that_type_mask;
}
-std::string Attribute::MaskString() const {
+std::string Attribute::MaskString(uint32_t type_mask) {
if (type_mask == android::ResTable_map::TYPE_ANY) {
return "any";
}
@@ -650,6 +653,10 @@
return out.str();
}
+std::string Attribute::MaskString() const {
+ return MaskString(type_mask);
+}
+
void Attribute::Print(std::ostream* out) const {
*out << "(attr) " << MaskString();
@@ -1017,6 +1024,21 @@
<< " [" << util::Joiner(entries, ", ") << "]";
}
+bool Macro::Equals(const Value* value) const {
+ const Macro* other = ValueCast<Macro>(value);
+ if (!other) {
+ return false;
+ }
+ return other->raw_value == raw_value && other->style_string.spans == style_string.spans &&
+ other->style_string.str == style_string.str &&
+ other->untranslatable_sections == untranslatable_sections &&
+ other->alias_namespaces == alias_namespaces;
+}
+
+void Macro::Print(std::ostream* out) const {
+ *out << "(macro) ";
+}
+
bool operator<(const Reference& a, const Reference& b) {
int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
if (cmp != 0) return cmp < 0;
@@ -1149,4 +1171,9 @@
return CopyValueFields(std::move(new_value), value);
}
+std::unique_ptr<Macro> CloningValueTransformer::TransformDerived(const Macro* value) {
+ auto new_value = std::make_unique<Macro>(*value);
+ return CopyValueFields(std::move(new_value), value);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 025864d..d11b013 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -164,6 +164,8 @@
Reference::Type reference_type;
bool private_reference = false;
bool is_dynamic = false;
+ std::optional<uint32_t> type_flags;
+ bool allow_raw;
Reference();
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
@@ -311,6 +313,8 @@
bool IsCompatibleWith(const Attribute& attr) const;
std::string MaskString() const;
+ static std::string MaskString(uint32_t type_mask);
+
void Print(std::ostream* out) const override;
bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
};
@@ -362,6 +366,28 @@
void MergeWith(Styleable* styleable);
};
+struct Macro : public TransformableValue<Macro, BaseValue<Macro>> {
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+
+ struct Namespace {
+ std::string alias;
+ std::string package_name;
+ bool is_private;
+
+ bool operator==(const Namespace& right) const {
+ return alias == right.alias && package_name == right.package_name &&
+ is_private == right.is_private;
+ }
+ };
+
+ std::vector<Namespace> alias_namespaces;
+
+ bool Equals(const Value* value) const override;
+ void Print(std::ostream* out) const override;
+};
+
template <typename T>
typename std::enable_if<std::is_base_of<Value, T>::value, std::ostream&>::type operator<<(
std::ostream& out, const std::unique_ptr<T>& value) {
@@ -388,6 +414,7 @@
std::unique_ptr<Array> TransformDerived(const Array* value) override;
std::unique_ptr<Plural> TransformDerived(const Plural* value) override;
std::unique_ptr<Styleable> TransformDerived(const Styleable* value) override;
+ std::unique_ptr<Macro> TransformDerived(const Macro* value) override;
};
} // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 4247ec5..b45c040 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -273,6 +273,7 @@
Styleable styleable = 3;
Array array = 4;
Plural plural = 5;
+ MacroBody macro = 6;
}
}
@@ -304,6 +305,13 @@
// Whether this reference is dynamic.
Boolean is_dynamic = 5;
+
+ // The type flags used when compiling the reference. Used for substituting the contents of macros.
+ uint32 type_flags = 6;
+
+ // Whether raw string values would have been accepted in place of this reference definition. Used
+ // for substituting the contents of macros.
+ bool allow_raw = 7;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -591,3 +599,32 @@
// The optional interpreted/compiled version of the `value` string.
Item compiled_item = 6;
}
+
+message MacroBody {
+ string raw_string = 1;
+ StyleString style_string = 2;
+ repeated UntranslatableSection untranslatable_sections = 3;
+ repeated NamespaceAlias namespace_stack = 4;
+ SourcePosition source = 5;
+}
+
+message NamespaceAlias {
+ string prefix = 1;
+ string package_name = 2;
+ bool is_private = 3;
+}
+
+message StyleString {
+ message Span {
+ string name = 1;
+ uint32 start_index = 2;
+ uint32 end_index = 3;
+ }
+ string str = 1;
+ repeated Span spans = 2;
+}
+
+message UntranslatableSection {
+ uint64 start_index = 1;
+ uint64 end_index = 2;
+}
\ No newline at end of file
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 1006ca9..3457e0b 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -36,6 +36,10 @@
std::string name;
uint32_t first_char;
uint32_t last_char;
+
+ bool operator==(const Span& right) const {
+ return name == right.name && first_char == right.first_char && last_char == right.last_char;
+ }
};
struct StyleString {
diff --git a/tools/aapt2/ValueTransformer.cpp b/tools/aapt2/ValueTransformer.cpp
index 6eb2e30..2d7996b 100644
--- a/tools/aapt2/ValueTransformer.cpp
+++ b/tools/aapt2/ValueTransformer.cpp
@@ -46,5 +46,6 @@
VALUE_CREATE_VALUE_DECL(Array);
VALUE_CREATE_VALUE_DECL(Plural);
VALUE_CREATE_VALUE_DECL(Styleable);
+VALUE_CREATE_VALUE_DECL(Macro);
} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/ValueTransformer.h b/tools/aapt2/ValueTransformer.h
index 6925111..6fc4a19 100644
--- a/tools/aapt2/ValueTransformer.h
+++ b/tools/aapt2/ValueTransformer.h
@@ -37,6 +37,7 @@
struct Array;
struct Plural;
struct Styleable;
+struct Macro;
#define AAPT_TRANSFORM_VALUE(T) \
virtual std::unique_ptr<T> TransformDerived(const T* value) = 0; \
@@ -97,6 +98,7 @@
AAPT_TRANSFORM_VALUE(Array);
AAPT_TRANSFORM_VALUE(Plural);
AAPT_TRANSFORM_VALUE(Styleable);
+ AAPT_TRANSFORM_VALUE(Macro);
protected:
StringPool* const pool_;
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 4e74ec3..d0c9d89 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -43,6 +43,9 @@
virtual void Visit(Array* value) { VisitAny(value); }
virtual void Visit(Plural* value) { VisitAny(value); }
virtual void Visit(Styleable* value) { VisitAny(value); }
+ virtual void Visit(Macro* value) {
+ VisitAny(value);
+ }
};
// Const version of ValueVisitor.
@@ -92,6 +95,9 @@
virtual void Visit(const Styleable* value) {
VisitAny(value);
}
+ virtual void Visit(const Macro* value) {
+ VisitAny(value);
+ }
};
// NOLINT, do not add parentheses around T.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 2c57fb2..e4d0f3b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -462,7 +462,7 @@
// that existing projects have out-of-date references which pass compilation.
xml::StripAndroidStudioAttributes(doc->root.get());
- XmlReferenceLinker xml_linker;
+ XmlReferenceLinker xml_linker(table);
if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
return {};
}
@@ -2112,7 +2112,7 @@
std::unique_ptr<xml::XmlResource> split_manifest =
GenerateSplitManifest(app_info_, *split_constraints_iter);
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(&final_table_);
if (!linker.Consume(context_, split_manifest.get())) {
context_->GetDiagnostics()->Error(DiagMessage()
<< "failed to create Split AndroidManifest.xml");
@@ -2143,7 +2143,7 @@
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
- XmlReferenceLinker manifest_linker;
+ XmlReferenceLinker manifest_linker(&final_table_);
if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
if (options_.generate_proguard_rules_path &&
!proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index d1e6d39..3118eb8 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -24,6 +24,7 @@
using testing::Eq;
using testing::HasSubstr;
+using testing::IsNull;
using testing::Ne;
using testing::NotNull;
@@ -532,4 +533,109 @@
EXPECT_THAT(*result, Eq(0x01fd0072));
}
+TEST_F(LinkTest, MacroSubstitution) {
+ StdErrDiagnostics diag;
+ const std::string values =
+ R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
+ <macro name="is_enabled">true</macro>
+ <macro name="deep_is_enabled">@macro/is_enabled</macro>
+ <macro name="attr_ref">?is_enabled_attr</macro>
+ <macro name="raw_string">Hello World!</macro>
+ <macro name="android_ref">@an:color/primary_text_dark</macro>
+
+ <attr name="is_enabled_attr" />
+ <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
+
+ <string name="is_enabled_str">@macro/is_enabled</string>
+ <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
+
+ <array name="my_array">
+ <item>@macro/is_enabled</item>
+ </array>
+
+ <style name="MyStyle">
+ <item name="android:background">@macro/attr_ref</item>
+ <item name="android:fontFamily">@macro/raw_string</item>
+ </style>
+ </resources>)";
+
+ const std::string xml_values =
+ R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@macro/android_ref"
+ android:fontFamily="@macro/raw_string">
+ </SomeLayout>)";
+
+ // Build a library with a public attribute
+ const std::string lib_res = GetTestPath("test-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
+
+ const std::string lib_apk = GetTestPath("test.apk");
+ // clang-format off
+ auto lib_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+ .AddCompiledResDir(lib_res, &diag)
+ .AddFlag("--no-auto-version")
+ .Build(lib_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(lib_link_args, &diag));
+
+ auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
+ ASSERT_THAT(apk, NotNull());
+
+ // Test that the type flags determines the value type
+ auto actual_bool =
+ test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
+ ASSERT_THAT(actual_bool, NotNull());
+ EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
+ EXPECT_EQ(0xffffffffu, actual_bool->value.data);
+
+ auto actual_str =
+ test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
+ ASSERT_THAT(actual_str, NotNull());
+ EXPECT_EQ(*actual_str->value, "true");
+
+ // Test nested data structures
+ auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
+ ASSERT_THAT(actual_array, NotNull());
+ EXPECT_THAT(actual_array->elements.size(), Eq(1));
+
+ auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
+ ASSERT_THAT(array_el_ref, NotNull());
+ EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
+ EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
+
+ auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
+ ASSERT_THAT(actual_style, NotNull());
+ EXPECT_THAT(actual_style->entries.size(), Eq(2));
+
+ {
+ auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
+ ASSERT_THAT(style_el, NotNull());
+ EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
+ EXPECT_THAT(style_el->id, Eq(0x7f010000));
+ }
+
+ {
+ auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
+ ASSERT_THAT(style_el, NotNull());
+ EXPECT_THAT(*style_el->value, Eq("Hello World!"));
+ }
+
+ // Test substitution in compiled xml files
+ auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
+ ASSERT_THAT(xml, NotNull());
+
+ auto& xml_attrs = xml->root->attributes;
+ ASSERT_THAT(xml_attrs.size(), Eq(2));
+
+ auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
+ ASSERT_THAT(attr_value, NotNull());
+ EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
+ EXPECT_THAT(attr_value->id, Eq(0x01060001));
+
+ EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
+ EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 17d11a6..74ecf47 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -567,13 +567,10 @@
}
bool FlattenTypes(BigBuffer* buffer) {
- // Sort the types by their IDs. They will be inserted into the StringPool in
- // this order.
-
size_t expected_type_id = 1;
for (const ResourceTableTypeView& type : package_.types) {
- if (type.type == ResourceType::kStyleable) {
- // Styleables aren't real Resource Types, they are represented in the R.java file.
+ if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) {
+ // Styleables and macros are not real resource types.
continue;
}
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index c24488b..d97e888 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -222,7 +222,7 @@
android:id="@id/foo"
app:foo="@id/foo" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
// The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 498d5a2..ec331df 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -656,6 +656,38 @@
}
out_ref->name = name_ref.ToResourceName();
}
+ if (pb_ref.type_flags() != 0) {
+ out_ref->type_flags = pb_ref.type_flags();
+ }
+ out_ref->allow_raw = pb_ref.allow_raw();
+ return true;
+}
+
+static bool DeserializeMacroFromPb(const pb::MacroBody& pb_ref, Macro* out_ref,
+ std::string* out_error) {
+ out_ref->raw_value = pb_ref.raw_string();
+
+ if (pb_ref.has_style_string()) {
+ out_ref->style_string.str = pb_ref.style_string().str();
+ for (const auto& span : pb_ref.style_string().spans()) {
+ out_ref->style_string.spans.emplace_back(Span{
+ .name = span.name(), .first_char = span.start_index(), .last_char = span.end_index()});
+ }
+ }
+
+ for (const auto& untranslatable_section : pb_ref.untranslatable_sections()) {
+ out_ref->untranslatable_sections.emplace_back(
+ UntranslatableSection{.start = static_cast<size_t>(untranslatable_section.start_index()),
+ .end = static_cast<size_t>(untranslatable_section.end_index())});
+ }
+
+ for (const auto& namespace_decls : pb_ref.namespace_stack()) {
+ out_ref->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = namespace_decls.prefix(),
+ .package_name = namespace_decls.package_name(),
+ .is_private = namespace_decls.is_private()});
+ }
+
return true;
}
@@ -801,6 +833,15 @@
value = std::move(plural);
} break;
+ case pb::CompoundValue::kMacro: {
+ const pb::MacroBody& pb_macro = pb_compound_value.macro();
+ auto macro = std::make_unique<Macro>();
+ if (!DeserializeMacroFromPb(pb_macro, macro.get(), out_error)) {
+ return {};
+ }
+ value = std::move(macro);
+ } break;
+
default:
LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
break;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index f13f82d..d2f0336 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -440,6 +440,36 @@
if (ref.is_dynamic) {
pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
}
+ if (ref.type_flags) {
+ pb_ref->set_type_flags(*ref.type_flags);
+ }
+ pb_ref->set_allow_raw(ref.allow_raw);
+}
+
+static void SerializeMacroToPb(const Macro& ref, pb::MacroBody* pb_macro) {
+ pb_macro->set_raw_string(ref.raw_value);
+
+ auto pb_style_str = pb_macro->mutable_style_string();
+ pb_style_str->set_str(ref.style_string.str);
+ for (const auto& span : ref.style_string.spans) {
+ auto pb_span = pb_style_str->add_spans();
+ pb_span->set_name(span.name);
+ pb_span->set_start_index(span.first_char);
+ pb_span->set_end_index(span.last_char);
+ }
+
+ for (const auto& untranslatable_section : ref.untranslatable_sections) {
+ auto pb_section = pb_macro->add_untranslatable_sections();
+ pb_section->set_start_index(untranslatable_section.start);
+ pb_section->set_end_index(untranslatable_section.end);
+ }
+
+ for (const auto& namespace_decls : ref.alias_namespaces) {
+ auto pb_namespace = pb_macro->add_namespace_stack();
+ pb_namespace->set_prefix(namespace_decls.alias);
+ pb_namespace->set_package_name(namespace_decls.package_name);
+ pb_namespace->set_is_private(namespace_decls.is_private);
+ }
}
template <typename T>
@@ -643,6 +673,11 @@
}
}
+ void Visit(const Macro* macro) override {
+ pb::MacroBody* pb_macro = out_value_->mutable_compound_value()->mutable_macro();
+ SerializeMacroToPb(*macro, pb_macro);
+ }
+
void VisitAny(const Value* unknown) override {
LOG(FATAL) << "unimplemented value: " << *unknown;
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 591ba149..e563eda 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -894,4 +894,38 @@
EXPECT_THAT(*(s->value), Eq("foo"));
}
+TEST(ProtoSerializeTest, SerializeMacro) {
+ auto original = std::make_unique<Macro>();
+ original->raw_value = "\nThis being human is a guest house.";
+ original->style_string.str = " This being human is a guest house.";
+ original->style_string.spans.emplace_back(Span{.name = "b", .first_char = 12, .last_char = 16});
+ original->untranslatable_sections.emplace_back(UntranslatableSection{.start = 12, .end = 17});
+ original->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = "prefix", .package_name = "package.name", .is_private = true});
+
+ CloningValueTransformer cloner(nullptr);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:macro/foo")
+ .SetValue(original->Transform(cloner))
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ Macro* deserialized = test::GetValue<Macro>(&new_table, "com.app.a:macro/foo");
+ ASSERT_THAT(deserialized, NotNull());
+ EXPECT_THAT(deserialized->raw_value, Eq(original->raw_value));
+ EXPECT_THAT(deserialized->style_string.str, Eq(original->style_string.str));
+ EXPECT_THAT(deserialized->style_string.spans, Eq(original->style_string.spans));
+ EXPECT_THAT(deserialized->untranslatable_sections, Eq(original->untranslatable_sections));
+ EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index e1e2e01..de6524d 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -616,8 +616,9 @@
for (const auto& package : table_->packages) {
for (const auto& type : package->types) {
- if (type->type == ResourceType::kAttrPrivate) {
- // We generate these as part of the kAttr type, so skip them here.
+ if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) {
+ // We generate kAttrPrivate as part of the kAttr type, so skip them here.
+ // Macros are not actual resources, so skip them as well.
continue;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index d08b61e..40395ed 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -570,4 +570,25 @@
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
}
+TEST(JavaClassGeneratorTest, SkipMacros) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+
+ std::string output;
+ StringOutputStream out(&output);
+ EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, Not(HasSubstr("bar")));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b7dfec3..e104066 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -264,7 +264,7 @@
</View>)");
foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
- XmlReferenceLinker xml_linker;
+ XmlReferenceLinker xml_linker(nullptr);
ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index c9b8d39..be6c930 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -133,12 +133,14 @@
// Once an XmlResource is processed by this linker, it is ready to be flattened.
class XmlReferenceLinker : public IXmlResourceConsumer {
public:
- XmlReferenceLinker() = default;
+ explicit XmlReferenceLinker(ResourceTable* table) : table_(table) {
+ }
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
private:
DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
+ ResourceTable* table_;
};
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8e49fab..4ac25bd 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -21,6 +21,7 @@
#include "androidfw/ResourceTypes.h"
#include "Diagnostics.h"
+#include "ResourceParser.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -37,128 +38,153 @@
using ::android::base::StringPrintf;
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.
-class ReferenceLinkerVisitor : public DescendingValueVisitor {
- public:
- using DescendingValueVisitor::Visit;
-
- ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
- StringPool* string_pool, xml::IPackageDeclStack* decl)
- : callsite_(callsite),
- context_(context),
- symbols_(symbols),
- package_decls_(decl),
- string_pool_(string_pool) {}
-
- void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
- error_ = true;
- }
+struct LoggingResourceName {
+ LoggingResourceName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls)
+ : ref_(ref), callsite_(callsite), decls_(decls) {
}
- // 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());
+ const Reference& ref_;
+ const CallSite& callsite_;
+ const xml::IPackageDeclStack* decls_;
+};
+
+inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
+ if (!name.ref_.name) {
+ out << name.ref_.id.value();
+ return out;
+ }
+
+ out << name.ref_.name.value();
+
+ Reference fully_qualified = name.ref_;
+ xml::ResolvePackage(name.decls_, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = name.callsite_.package;
+ }
+
+ if (full_name != name.ref_.name.value()) {
+ out << " (aka " << full_name << ")";
+ }
+ return out;
+}
+
+} // namespace
+
+std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
+ auto linked_item =
+ ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+ if (linked_item) {
+ auto linked_item_ptr = linked_item.release();
+ if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
+ return std::unique_ptr<Reference>(ref);
}
+ context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
+ << "value of '"
+ << LoggingResourceName(*value, callsite_, package_decls_)
+ << "' must be a resource reference");
+ delete linked_item_ptr;
+ }
- for (Style::Entry& entry : style->entries) {
- std::string err_str;
+ error_ = true;
+ return CloningValueTransformer::TransformDerived(value);
+}
- // 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;
- ResolvePackage(package_decls_, &transformed_reference);
+std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
+ // We visit the Style specially because during this phase, values of attributes are either
+ // RawString or Reference 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.
+ auto new_style = CloningValueTransformer::TransformDerived(style);
+ if (new_style->parent) {
+ new_style->parent = *TransformDerived(&style->parent.value());
+ }
- // 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_, context_, symbols_, &err_str);
- if (symbol) {
- // Assign our style key the correct ID. The ID may not exist.
- entry.key.id = symbol->id;
+ for (Style::Entry& entry : new_style->entries) {
+ std::string err_str;
- // 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());
+ // 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;
+ ResolvePackage(package_decls_, &transformed_reference);
- // Link/resolve the final value (mostly if it's a reference).
- entry.value->Accept(this);
+ // 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_, context_, symbols_, &err_str);
+ if (symbol) {
+ // Assign our style key the correct ID. The ID may not exist.
+ entry.key.id = symbol->id;
- // Now verify that the type of this item is compatible with the
- // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
- // check is fast and we avoid creating a DiagMessage when the match is successful.
- if (!symbol->attribute->Matches(*entry.value, nullptr)) {
- // The actual type of this item is incompatible with the attribute.
- DiagMessage msg(entry.key.GetSource());
+ // Link/resolve the final value if it's a reference.
+ entry.value = entry.value->Transform(*this);
- // 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;
- }
+ // 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());
- } else {
+ // Now verify that the type of this item is compatible with the
+ // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
+ // check is fast and we avoid creating a DiagMessage when the match is successful.
+ if (!symbol->attribute->Matches(*entry.value, nullptr)) {
+ // The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
- msg << "style attribute '";
- ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
- msg << "' " << err_str;
+
+ // 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;
}
+ } else {
+ context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
+ << "style attribute '"
+ << LoggingResourceName(entry.key, callsite_, package_decls_)
+ << "' " << err_str);
+
+ error_ = true;
}
}
+ return new_style;
+}
- bool HasError() {
- return error_;
+std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
+ auto linked_value =
+ ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+ if (linked_value) {
+ return linked_value;
}
+ error_ = true;
+ return CloningValueTransformer::TransformDerived(value);
+}
- 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.
+std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
+ std::unique_ptr<Item> value, const Attribute* attr) {
+ if (RawString* raw_string = ValueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
- // 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())) {
- std::unique_ptr<Item> transformed =
- ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
-
- // If we could not parse as any specific type, try a basic STRING.
- if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
- StringBuilder string_builder;
- string_builder.AppendText(*raw_string->value);
- if (string_builder) {
- transformed =
- util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
- }
- }
-
- if (transformed) {
- return transformed;
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+ StringBuilder string_builder;
+ string_builder.AppendText(*raw_string->value);
+ if (string_builder) {
+ transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
}
}
- return value;
- }
- const CallSite& callsite_;
- IAaptContext* context_;
- SymbolTable* symbols_;
- xml::IPackageDeclStack* package_decls_;
- StringPool* string_pool_;
- bool error_ = false;
-};
+ if (transformed) {
+ return transformed;
+ }
+ }
+ return value;
+}
+
+namespace {
class EmptyDeclStack : public xml::IPackageDeclStack {
public:
@@ -175,6 +201,27 @@
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
};
+struct MacroDeclStack : public xml::IPackageDeclStack {
+ explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
+ : alias_namespaces_(std::move(namespaces)) {
+ }
+
+ Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{{}, true /*private*/};
+ }
+ for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
+ if (alias == StringPiece(it->alias)) {
+ return xml::ExtractedPackage{it->package_name, it->is_private};
+ }
+ }
+ return {};
+ }
+
+ private:
+ std::vector<Macro::Namespace> alias_namespaces_;
+};
+
// 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,
@@ -220,8 +267,6 @@
// If the callsite package is the same as the current compilation package,
// check the feature split dependencies as well. Feature split resources
// can be referenced without a namespace, just like the base package.
- // TODO: modify the package name of included splits instead of having the
- // symbol table look up the resource in in every package. b/136105066
if (callsite.package == context->GetCompilationPackage()) {
const auto& split_name_dependencies = context->GetSplitNameDependencies();
for (const std::string& split_name : split_name_dependencies) {
@@ -295,29 +340,6 @@
return xml::AaptAttribute(*symbol->attribute, symbol->id);
}
-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;
- }
-
- *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) {
@@ -348,18 +370,71 @@
}
}
-bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
- IAaptContext* context, SymbolTable* symbols,
- const xml::IPackageDeclStack* decls) {
- CHECK(reference != nullptr);
- if (!reference->name && !reference->id) {
+std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
+ const Reference& reference,
+ IAaptContext* context, SymbolTable* symbols,
+ ResourceTable* table,
+ const xml::IPackageDeclStack* decls) {
+ if (!reference.name && !reference.id) {
// This is @null.
- return true;
+ return std::make_unique<Reference>(reference);
}
- Reference transformed_reference = *reference;
+ Reference transformed_reference = reference;
xml::ResolvePackage(decls, &transformed_reference);
+ if (transformed_reference.name.value().type == ResourceType::kMacro) {
+ if (transformed_reference.name.value().package.empty()) {
+ transformed_reference.name.value().package = callsite.package;
+ }
+
+ auto result = table->FindResource(transformed_reference.name.value());
+ if (!result || result.value().entry->values.empty()) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(reference.GetSource())
+ << "failed to find definition for "
+ << LoggingResourceName(transformed_reference, callsite, decls));
+ return {};
+ }
+
+ auto& macro_values = result.value().entry->values;
+ CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
+
+ auto macro = ValueCast<Macro>(macro_values[0]->value.get());
+ CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
+ << *macro_values[0]->value << ")";
+
+ // Re-create the state used to parse the macro tag to compile the macro contents as if it was
+ // defined inline
+ uint32_t type_flags = 0;
+ if (reference.type_flags.has_value()) {
+ type_flags = reference.type_flags.value();
+ }
+
+ MacroDeclStack namespace_stack(macro->alias_namespaces);
+ FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
+ .style_string = macro->style_string,
+ .untranslatable_sections = macro->untranslatable_sections,
+ .namespace_resolver = &namespace_stack,
+ .source = macro->GetSource()};
+
+ auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
+ macro_values[0]->config, *context->GetDiagnostics());
+ if (new_value == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(reference.GetSource())
+ << "failed to substitute macro "
+ << LoggingResourceName(transformed_reference, callsite, decls)
+ << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
+ return {};
+ }
+
+ if (auto ref = ValueCast<Reference>(new_value.get())) {
+ return LinkReference(callsite, *ref, context, symbols, table, decls);
+ }
+ return new_value;
+ }
+
std::string err_str;
const SymbolTable::Symbol* s =
ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
@@ -367,17 +442,17 @@
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
// Ex: Linking against own resources when building a static library.
- reference->id = s->id;
- reference->is_dynamic = s->is_dynamic;
- return true;
+ auto new_ref = std::make_unique<Reference>(reference);
+ new_ref->id = s->id;
+ new_ref->is_dynamic = s->is_dynamic;
+ return std::move(new_ref);
}
- DiagMessage error_msg(reference->GetSource());
- error_msg << "resource ";
- WriteResourceName(*reference, callsite, decls, &error_msg);
- error_msg << " " << err_str;
- context->GetDiagnostics()->Error(error_msg);
- return false;
+ context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
+ << "resource "
+ << LoggingResourceName(transformed_reference, callsite, decls)
+ << " " << err_str);
+ return {};
}
bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
@@ -412,14 +487,15 @@
// 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);
+ ReferenceLinkerTransformer reference_transformer(callsite, context,
+ context->GetExternalSymbols(),
+ &table->string_pool, table, &decl_stack);
for (auto& config_value : entry->values) {
- config_value->value->Accept(&visitor);
+ config_value->value = config_value->value->Transform(reference_transformer);
}
- if (visitor.HasError()) {
+ if (reference_transformer.HasError()) {
error = true;
}
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 1256709..770f1e5 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -28,6 +28,41 @@
namespace aapt {
+// A ValueTransformer that returns fully linked versions of resource and macro references.
+class ReferenceLinkerTransformer : public CloningValueTransformer {
+ public:
+ ReferenceLinkerTransformer(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+ StringPool* string_pool, ResourceTable* table,
+ xml::IPackageDeclStack* decl)
+ : CloningValueTransformer(string_pool),
+ callsite_(callsite),
+ context_(context),
+ symbols_(symbols),
+ table_(table),
+ package_decls_(decl) {
+ }
+
+ std::unique_ptr<Reference> TransformDerived(const Reference* value) override;
+ std::unique_ptr<Item> TransformItem(const Reference* value) override;
+ std::unique_ptr<Style> TransformDerived(const Style* value) override;
+
+ bool HasError() {
+ return error_;
+ }
+
+ private:
+ // 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);
+
+ const CallSite& callsite_;
+ IAaptContext* context_;
+ SymbolTable* symbols_;
+ ResourceTable* table_;
+ xml::IPackageDeclStack* package_decls_;
+ bool error_ = false;
+};
+
// 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.
@@ -70,19 +105,28 @@
// 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);
+ /*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, const xml::IPackageDeclStack* decls);
+ // Returns a fully linked version a resource reference.
+ //
+ // If the reference points to a non-macro resource, the xml::IPackageDeclStack is used to
+ // determine the fully qualified name of the referenced resource. If the symbol is visible
+ // to the reference at the callsite, a copy of the reference with an updated updated ID is
+ // returned.
+ //
+ // If the reference points to a macro, the ResourceTable is used to find the macro definition and
+ // substitute its contents in place of the reference.
+ //
+ // Returns nullptr on failure, and an error message is logged to the IDiagnostics in the context.
+ static std::unique_ptr<Item> LinkReference(const CallSite& callsite, const Reference& reference,
+ IAaptContext* context, SymbolTable* symbols,
+ ResourceTable* table,
+ const xml::IPackageDeclStack* decls);
// Links all references in the ResourceTable.
bool Consume(IAaptContext* context, ResourceTable* table) override;
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 228c5bd74..2d8f0d3 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -365,4 +365,22 @@
EXPECT_THAT(s, IsNull());
}
+TEST(ReferenceLinkerTest, MacroFailToFindDefinition) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddReference("com.app.test:string/foo", ResourceId(0x7f020000), "com.app.test:macro/bar")
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .Build();
+
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context.get(), table.get()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index a98ab0f..d638096 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -82,7 +82,7 @@
app:foo="16dp"
foo="bar"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -121,7 +121,7 @@
app:foo="16dp"
foo="bar"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -181,7 +181,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingHorizontal="24dp" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -256,7 +256,7 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
Item* padding_horizontal_value =
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index c3c16b9..aaa085e 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -33,49 +33,18 @@
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.
-class ReferenceVisitor : public DescendingValueVisitor {
- public:
- using DescendingValueVisitor::Visit;
-
- ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls)
- : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {}
-
- void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) {
- error_ = true;
- }
- }
-
- bool HasError() const {
- return error_;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
-
- const CallSite& callsite_;
- IAaptContext* context_;
- SymbolTable* symbols_;
- xml::IPackageDeclStack* decls_;
- bool error_;
-};
-
// Visits each xml Element and compiles the attributes within.
class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
- XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
- SymbolTable* symbols)
+ XmlVisitor(const Source& source, StringPool* pool, const CallSite& callsite,
+ IAaptContext* context, ResourceTable* table, SymbolTable* symbols)
: source_(source),
callsite_(callsite),
context_(context),
symbols_(symbols),
- reference_visitor_(callsite, context, symbols, this) {
+ reference_transformer_(callsite, context, symbols, pool, table, this) {
}
void Visit(xml::Element* el) override {
@@ -127,7 +96,7 @@
if (attr.compiled_value) {
// With a compiledValue, we must resolve the reference and assign it an ID.
attr.compiled_value->SetSource(source);
- attr.compiled_value->Accept(&reference_visitor_);
+ attr.compiled_value = attr.compiled_value->Transform(reference_transformer_);
} 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);
@@ -143,7 +112,7 @@
}
bool HasError() {
- return error_ || reference_visitor_.HasError();
+ return error_ || reference_transformer_.HasError();
}
private:
@@ -154,7 +123,7 @@
IAaptContext* context_;
SymbolTable* symbols_;
- ReferenceVisitor reference_visitor_;
+ ReferenceLinkerTransformer reference_transformer_;
bool error_ = false;
};
@@ -173,7 +142,8 @@
callsite.package = context->GetCompilationPackage();
}
- XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
+ XmlVisitor visitor(resource->file.source, &resource->string_pool, callsite, context, table_,
+ context->GetExternalSymbols());
if (resource->root) {
resource->root->Accept(&visitor);
return !visitor.HasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 0ce2e50..ddf5b9a 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -91,7 +91,7 @@
nonAaptAttrRef="@id/id"
class="hello" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -144,7 +144,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@android:color/hidden" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
}
@@ -153,7 +153,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@*android:color/hidden" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
}
@@ -162,7 +162,7 @@
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
support:colorAccent="#ff0000" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -181,7 +181,7 @@
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:colorAccent="@app:color/red" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -203,7 +203,7 @@
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
</View>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -239,7 +239,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
android:attr="@id/id"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -261,7 +261,7 @@
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<gradient />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
@@ -283,7 +283,7 @@
<gradient xmlns:android="http://schemas.android.com/apk/res/android"
android:angle="90"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
@@ -305,7 +305,7 @@
<gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
context_->SetMinSdkVersion(30);
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index a023494..182203d 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -177,6 +177,10 @@
return event_queue_.front().data2;
}
+const std::vector<XmlPullParser::PackageDecl>& XmlPullParser::package_decls() const {
+ return package_aliases_;
+}
+
XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
return event_queue_.front().attributes.begin();
}
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 6ebaa28..5da2d4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -123,6 +123,13 @@
*/
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
+
+ const std::vector<PackageDecl>& package_decls() const;
+
//
// Remaining methods are for retrieving information about attributes
// associated with a StartElement.
@@ -180,11 +187,6 @@
const std::string empty_;
size_t depth_;
std::stack<std::string> namespace_uris_;
-
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
std::vector<PackageDecl> package_aliases_;
};