diff options
796 files changed, 8908 insertions, 40240 deletions
diff --git a/Android.bp b/Android.bp index b3dc98b2fa06..6b55cc9f5ea8 100644 --- a/Android.bp +++ b/Android.bp @@ -633,6 +633,7 @@ java_library { "//frameworks/base/apex/blobstore/framework", "//frameworks/base/apex/jobscheduler/framework", "//frameworks/base/packages/Tethering/tests/unit", + "//packages/modules/Connectivity/Tethering/tests/unit", ], errorprone: { javacflags: [ diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java index 04feef485048..a8c0f0ec3e00 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java @@ -31,6 +31,8 @@ import android.util.IndentingPrintWriter; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; + import java.text.SimpleDateFormat; import java.util.Date; @@ -39,17 +41,23 @@ import java.util.Date; * expires. The timer will wake up the device if the alarm is a "wakeup" alarm. */ class Alarm { - private static final int NUM_POLICIES = 2; + @VisibleForTesting + public static final int NUM_POLICIES = 3; /** * Index used to store the time the alarm was requested to expire. To be used with - * {@link #setPolicyElapsed(int, long)} + * {@link #setPolicyElapsed(int, long)}. */ public static final int REQUESTER_POLICY_INDEX = 0; /** * Index used to store the earliest time the alarm can expire based on app-standby policy. - * To be used with {@link #setPolicyElapsed(int, long)} + * To be used with {@link #setPolicyElapsed(int, long)}. */ public static final int APP_STANDBY_POLICY_INDEX = 1; + /** + * Index used to store the earliest time the alarm can expire based on the device's doze policy. + * To be used with {@link #setPolicyElapsed(int, long)}. + */ + public static final int DEVICE_IDLE_POLICY_INDEX = 2; public final int type; /** @@ -128,11 +136,19 @@ class Alarm { * @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX}, * {@link #APP_STANDBY_POLICY_INDEX}]. */ - public long getPolicyElapsed(int policyIndex) { + @VisibleForTesting + long getPolicyElapsed(int policyIndex) { return mPolicyWhenElapsed[policyIndex]; } /** + * @return the time this alarm was requested to go off in the elapsed time base. + */ + public long getRequestedElapsed() { + return mPolicyWhenElapsed[REQUESTER_POLICY_INDEX]; + } + + /** * Get the earliest time that this alarm should be delivered to the requesting app. */ public long getWhenElapsed() { @@ -205,8 +221,10 @@ class Alarm { return "requester"; case APP_STANDBY_POLICY_INDEX: return "app_standby"; + case DEVICE_IDLE_POLICY_INDEX: + return "device_idle"; default: - return "unknown"; + return "--unknown--"; } } 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 f196567bc391..c8a04d674739 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -21,12 +21,14 @@ import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; +import static android.app.AlarmManager.FLAG_IDLE_UNTIL; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; +import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; import android.annotation.NonNull; @@ -81,7 +83,6 @@ import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongArrayQueue; -import android.util.MutableBoolean; import android.util.NtpTrustedTime; import android.util.Pair; import android.util.Slog; @@ -699,36 +700,31 @@ public class AlarmManagerService extends SystemService { final HashMap<String, PriorityClass> mPriorities = new HashMap<>(); int mCurrentSeq = 0; - static final class WakeupEvent { - public long when; - public int uid; - public String action; - - public WakeupEvent(long theTime, int theUid, String theAction) { - when = theTime; - uid = theUid; - action = theAction; - } - } - final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() { @Override public int compare(Alarm lhs, Alarm rhs) { - // priority class trumps everything. TICK < WAKEUP < NORMAL + + // Alarm to exit device_idle should go out first. + final boolean lhsIdleUntil = (lhs.flags & FLAG_IDLE_UNTIL) != 0; + final boolean rhsIdleUntil = (rhs.flags & FLAG_IDLE_UNTIL) != 0; + if (lhsIdleUntil != rhsIdleUntil) { + return lhsIdleUntil ? -1 : 1; + } + + // Then, priority class trumps everything. TICK < WAKEUP < NORMAL if (lhs.priorityClass.priority < rhs.priorityClass.priority) { return -1; } else if (lhs.priorityClass.priority > rhs.priorityClass.priority) { return 1; } - // within each class, sort by nominal delivery time - if (lhs.getWhenElapsed() < rhs.getWhenElapsed()) { + // within each class, sort by requested delivery time + if (lhs.getRequestedElapsed() < rhs.getRequestedElapsed()) { return -1; - } else if (lhs.getWhenElapsed() > rhs.getWhenElapsed()) { + } else if (lhs.getRequestedElapsed() > rhs.getRequestedElapsed()) { return 1; } - // same priority class + same target delivery time return 0; } }; @@ -777,10 +773,9 @@ public class AlarmManagerService extends SystemService { final AlarmStore mAlarmStore; // set to non-null if in idle mode; while in this mode, any alarms we don't want - // to run during this time are placed in mPendingWhileIdleAlarms + // to run during this time are rescehduled to go off after this alarm. Alarm mPendingIdleUntil = null; Alarm mNextWakeFromIdle = null; - ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>(); @VisibleForTesting AlarmManagerService(Context context, Injector injector) { @@ -830,15 +825,21 @@ public class AlarmManagerService extends SystemService { return restoreRequestedTime(a); }); - if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) { - // The next wake from idle got updated due to the rtc time change, implying we need - // to update the time we have to come out of idle too. - changed |= mAlarmStore.updateAlarmDeliveries(a -> { - if (a != mPendingIdleUntil) { - return false; + if (changed && mPendingIdleUntil != null) { + if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) { + // The next wake from idle got updated due to the rtc time change, so we need + // to update the time we have to come out of idle too. + final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(a -> { + if (a != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(a); + }); + if (idleUntilUpdated) { + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); } - return adjustIdleUntilTime(a); - }); + } } if (changed) { @@ -972,11 +973,10 @@ public class AlarmManagerService extends SystemService { // Recurring alarms may have passed several alarm intervals while the // alarm was kept pending. Send the appropriate trigger count. if (alarm.repeatInterval > 0) { - alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX)) - / alarm.repeatInterval; + alarm.count += (nowELAPSED - alarm.getRequestedElapsed()) / alarm.repeatInterval; // Also schedule its next recurrence final long delta = alarm.count * alarm.repeatInterval; - final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta; + final long nextElapsed = alarm.getRequestedElapsed() + delta; final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval); setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed, @@ -1015,25 +1015,6 @@ public class AlarmManagerService extends SystemService { } } - void restorePendingWhileIdleAlarmsLocked() { - if (RECORD_DEVICE_IDLE_ALARMS) { - IdleDispatchEntry ent = new IdleDispatchEntry(); - ent.uid = 0; - ent.pkg = "FINISH IDLE"; - ent.elapsedRealtime = mInjector.getElapsedRealtime(); - mAllowWhileIdleDispatches.add(ent); - } - - // Bring pending alarms back into the main list. - if (mPendingWhileIdleAlarms.size() > 0) { - ArrayList<Alarm> alarms = mPendingWhileIdleAlarms; - mPendingWhileIdleAlarms = new ArrayList<>(); - for (int i = alarms.size() - 1; i >= 0; i--) { - setImplLocked(alarms.get(i)); - } - } - } - static final class InFlight { final PendingIntent mPendingIntent; final long mWhenElapsed; @@ -1571,7 +1552,7 @@ public class AlarmManagerService extends SystemService { return false; } restoreRequestedTime(alarm); - long triggerBeforeFuzz = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX); + long triggerBeforeFuzz = alarm.getRequestedElapsed(); if (mNextWakeFromIdle != null && triggerBeforeFuzz > mNextWakeFromIdle.getWhenElapsed()) { triggerBeforeFuzz = mNextWakeFromIdle.getWhenElapsed(); } @@ -1591,6 +1572,35 @@ public class AlarmManagerService extends SystemService { } /** + * Adjusts the delivery time of the alarm based on device_idle (doze) rules. + * + * @param alarm The alarm to adjust + * @return {@code true} if the alarm delivery time was updated. + */ + private boolean adjustDeliveryTimeBasedOnDeviceIdle(Alarm alarm) { + final long nowElapsed = mInjector.getElapsedRealtime(); + if (mPendingIdleUntil == null || mPendingIdleUntil == alarm) { + return alarm.setPolicyElapsed(DEVICE_IDLE_POLICY_INDEX, nowElapsed); + } + + final long deviceIdlePolicyTime; + if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED + | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) { + // Unrestricted. + deviceIdlePolicyTime = nowElapsed; + } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + // Allowed but limited. + final long lastDispatch = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0); + deviceIdlePolicyTime = (lastDispatch == 0) ? nowElapsed + : lastDispatch + mConstants.ALLOW_WHILE_IDLE_LONG_TIME; + } else { + // Not allowed. + deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed(); + } + return alarm.setPolicyElapsed(DEVICE_IDLE_POLICY_INDEX, deviceIdlePolicyTime); + } + + /** * Adjusts the alarm's policy time for app_standby. * * @param alarm The alarm to update. @@ -1643,12 +1653,6 @@ public class AlarmManagerService extends SystemService { return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } - private static boolean isAllowedWhileIdle(Alarm a) { - return ((a.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE - | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED - | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0); - } - private void setImplLocked(Alarm a) { if ((a.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) { adjustIdleUntilTime(a); @@ -1676,16 +1680,9 @@ public class AlarmManagerService extends SystemService { mAlarmStore.remove(mPendingIdleUntil::equals); } mPendingIdleUntil = a; - final ArrayList<Alarm> notAllowedWhileIdleAlarms = mAlarmStore.remove( - alarm -> !isAllowedWhileIdle(alarm)); - mPendingWhileIdleAlarms.addAll(notAllowedWhileIdleAlarms); + mAlarmStore.updateAlarmDeliveries(alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); } else if (mPendingIdleUntil != null) { - // We currently have an idle until alarm scheduled; if the new alarm has - // not explicitly stated it wants to run while idle, then put it on hold. - if (!isAllowedWhileIdle(a)) { - mPendingWhileIdleAlarms.add(a); - return; - } + adjustDeliveryTimeBasedOnDeviceIdle(a); } if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed() @@ -1694,12 +1691,17 @@ public class AlarmManagerService extends SystemService { // If this wake from idle is earlier than whatever was previously scheduled, // and we are currently idling, then the idle-until time needs to be updated. if (mPendingIdleUntil != null) { - mAlarmStore.updateAlarmDeliveries(alarm -> { + final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> { if (alarm != mPendingIdleUntil) { return false; } return adjustIdleUntilTime(alarm); }); + if (updated) { + // idle-until got updated, so also update all alarms not allowed while idle. + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); + } } } } @@ -2095,7 +2097,7 @@ public class AlarmManagerService extends SystemService { mAppWakeupHistory.dump(pw, nowELAPSED); - if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) { + if (mPendingIdleUntil != null) { pw.println(); pw.println("Idle mode state:"); @@ -2107,8 +2109,6 @@ public class AlarmManagerService extends SystemService { } else { pw.println("null"); } - pw.println("Pending alarms:"); - dumpAlarmList(pw, mPendingWhileIdleAlarms, nowELAPSED, sdf); pw.decreaseIndent(); } if (mNextWakeFromIdle != null) { @@ -2428,10 +2428,6 @@ public class AlarmManagerService extends SystemService { mPendingIdleUntil.dumpDebug( proto, AlarmManagerServiceDumpProto.PENDING_IDLE_UNTIL, nowElapsed); } - for (Alarm a : mPendingWhileIdleAlarms) { - a.dumpDebug(proto, AlarmManagerServiceDumpProto.PENDING_WHILE_IDLE_ALARMS, - nowElapsed); - } if (mNextWakeFromIdle != null) { mNextWakeFromIdle.dumpDebug(proto, AlarmManagerServiceDumpProto.NEXT_WAKE_FROM_IDLE, nowElapsed); @@ -2752,21 +2748,13 @@ public class AlarmManagerService extends SystemService { return; } - final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver); - final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms); + final ArrayList<Alarm> removedAlarms = mAlarmStore.remove( + a -> a.matches(operation, directReceiver)); for (final Alarm removed : removedAlarms) { decrementAlarmCount(removed.uid, 1); } final boolean didRemove = !removedAlarms.isEmpty(); - for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { - final Alarm alarm = mPendingWhileIdleAlarms.get(i); - if (alarm.matches(operation, directReceiver)) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingWhileIdleAlarms.remove(i); - decrementAlarmCount(alarm.uid, 1); - } - } 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--) { @@ -2793,22 +2781,25 @@ public class AlarmManagerService extends SystemService { if (DEBUG_BATCH) { Slog.v(TAG, "remove(operation) changed bounds; rebatching"); } - boolean restorePending = false; + boolean idleUntilUpdated = false; if (mPendingIdleUntil != null && mPendingIdleUntil.matches(operation, directReceiver)) { mPendingIdleUntil = null; - restorePending = true; + idleUntilUpdated = true; } if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) { mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); + if (mPendingIdleUntil != null) { + idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> { + if (alarm != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(alarm); + }); + } } - if (restorePending) { - restorePendingWhileIdleAlarmsLocked(); + if (idleUntilUpdated) { + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); @@ -2821,21 +2812,12 @@ public class AlarmManagerService extends SystemService { return; } - final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid; - final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms); + final ArrayList<Alarm> removed = mAlarmStore.remove(a -> a.uid == uid); final boolean didRemove = !removed.isEmpty(); if (didRemove) { decrementAlarmCount(uid, removed.size()); } - for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { - final Alarm a = mPendingWhileIdleAlarms.get(i); - if (a.uid == uid) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingWhileIdleAlarms.remove(i); - decrementAlarmCount(uid, 1); - } - } 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--) { @@ -2850,20 +2832,26 @@ public class AlarmManagerService extends SystemService { } // If we're currently using this app's alarms to come out of doze, // make sure to reset to any remaining WAKE_FROM_IDLE alarms. + boolean idleUntilUpdated = false; if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) { mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); + if (mPendingIdleUntil != null) { + idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> { + if (alarm != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(alarm); + }); + } } if (mPendingIdleUntil != null && mPendingIdleUntil.uid == uid) { // Should never happen - only the system uid is allowed to set idle-until alarms Slog.wtf(TAG, "Removed app uid " + uid + " set idle-until alarm!"); mPendingIdleUntil = null; - restorePendingWhileIdleAlarmsLocked(); + idleUntilUpdated = true; + } + if (idleUntilUpdated) { + mAlarmStore.updateAlarmDeliveries(alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); } if (didRemove) { if (DEBUG_BATCH) { @@ -2883,29 +2871,12 @@ public class AlarmManagerService extends SystemService { return; } - final MutableBoolean removedNextWakeFromIdle = new MutableBoolean(false); - final Predicate<Alarm> whichAlarms = (Alarm a) -> { - final boolean didMatch = a.matches(packageName); - if (didMatch && a == mNextWakeFromIdle) { - removedNextWakeFromIdle.value = true; - } - return didMatch; - }; - - final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms); + final ArrayList<Alarm> removed = mAlarmStore.remove(a -> a.matches(packageName)); final boolean didRemove = !removed.isEmpty(); if (didRemove) { decrementAlarmCount(removed.get(0).uid, removed.size()); } - for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { - final Alarm a = mPendingWhileIdleAlarms.get(i); - if (a.matches(packageName)) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingWhileIdleAlarms.remove(i); - decrementAlarmCount(a.uid, 1); - } - } 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--) { @@ -2921,14 +2892,20 @@ public class AlarmManagerService extends SystemService { } // If we're currently using this app's alarms to come out of doze, // make sure to reset to any remaining WAKE_FROM_IDLE alarms. - if (removedNextWakeFromIdle.value) { + if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(packageName)) { mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; + if (mPendingIdleUntil != null) { + final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> { + if (alarm != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(alarm); + }); + if (updated) { + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); } - return adjustIdleUntilTime(alarm); - }); + } } if (didRemove) { if (DEBUG_BATCH) { @@ -2953,14 +2930,6 @@ public class AlarmManagerService extends SystemService { decrementAlarmCount(uid, removed.size()); } - for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { - final Alarm a = mPendingWhileIdleAlarms.get(i); - if (a.uid == uid) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingWhileIdleAlarms.remove(i); - decrementAlarmCount(uid, 1); - } - } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { if (mPendingBackgroundAlarms.keyAt(i) == uid) { final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i); @@ -2992,14 +2961,6 @@ public class AlarmManagerService extends SystemService { } final boolean didRemove = !removedAlarms.isEmpty(); - for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { - if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).creatorUid) - == userHandle) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - final Alarm removed = mPendingWhileIdleAlarms.remove(i); - decrementAlarmCount(removed.uid, 1); - } - } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { if (UserHandle.getUserId(mPendingBackgroundAlarms.keyAt(i)) == userHandle) { final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i); @@ -3016,6 +2977,21 @@ public class AlarmManagerService extends SystemService { mLastAllowWhileIdleDispatch.removeAt(i); } } + if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) { + mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); + if (mPendingIdleUntil != null) { + final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> { + if (alarm != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(alarm); + }); + if (updated) { + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); + } + } + } if (didRemove) { if (DEBUG_BATCH) { @@ -3062,12 +3038,6 @@ public class AlarmManagerService extends SystemService { return true; } } - for (int i = 0; i < mPendingWhileIdleAlarms.size(); i++) { - final Alarm a = mPendingWhileIdleAlarms.get(i); - if (a.matches(packageName)) { - return true; - } - } return false; } @@ -3134,16 +3104,10 @@ public class AlarmManagerService extends SystemService { private static native long getNextAlarm(long nativeData, int type); private long getWhileIdleMinIntervalLocked(int uid) { - final boolean dozing = mPendingIdleUntil != null; final boolean ebs = (mAppStateTracker != null) && mAppStateTracker.isForceAllAppsStandbyEnabled(); - if (!dozing && !ebs) { - return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME; - } - if (dozing) { - return mConstants.ALLOW_WHILE_IDLE_LONG_TIME; - } - if (mUseAllowWhileIdleShortTime.get(uid)) { + + if (!ebs || mUseAllowWhileIdleShortTime.get(uid)) { // if the last allow-while-idle went off while uid was fg, or the uid // recently came into fg, don't block the alarm for long. return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME; @@ -3201,16 +3165,12 @@ public class AlarmManagerService extends SystemService { } if (mPendingIdleUntil == alarm) { mPendingIdleUntil = null; - restorePendingWhileIdleAlarmsLocked(); + mAlarmStore.updateAlarmDeliveries(a -> adjustDeliveryTimeBasedOnDeviceIdle(a)); } if (mNextWakeFromIdle == alarm) { mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); - mAlarmStore.updateAlarmDeliveries(a -> { - if (a != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(a); - }); + // Note that we don't need to update mPendingIdleUntil because it should already + // be removed from the alarm store. } // Recurring alarms may have passed several alarm intervals while the @@ -3218,11 +3178,10 @@ public class AlarmManagerService extends SystemService { if (alarm.repeatInterval > 0) { // this adjustment will be zero if we're late by // less than one full repeat interval - alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX)) - / alarm.repeatInterval; + alarm.count += (nowELAPSED - alarm.getRequestedElapsed()) / alarm.repeatInterval; // Also schedule its next recurrence final long delta = alarm.count * alarm.repeatInterval; - final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta; + final long nextElapsed = alarm.getRequestedElapsed() + delta; final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval); setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed, @@ -4222,6 +4181,12 @@ public class AlarmManagerService extends SystemService { if (allowWhileIdle) { // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED); + mAlarmStore.updateAlarmDeliveries(a -> { + if (a.creatorUid != alarm.creatorUid) { + return false; + } + return adjustDeliveryTimeBasedOnDeviceIdle(a); + }); if ((mAppStateTracker == null) || mAppStateTracker.isUidInForeground(alarm.creatorUid)) { mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index 227b8276abe1..56b97f2e5757 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -50,6 +50,9 @@ public final class TimeController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); + @VisibleForTesting + static final long DELAY_COALESCE_TIME_MS = 30_000L; + /** Deadline alarm tag for logging purposes */ private final String DEADLINE_TAG = "*job.deadline*"; /** Delay alarm tag for logging purposes */ @@ -57,6 +60,7 @@ public final class TimeController extends StateController { private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; + private volatile long mLastFiredDelayExpiredElapsedMillis; private final boolean mChainedAttributionEnabled; @@ -273,7 +277,6 @@ public final class TimeController extends StateController { @VisibleForTesting void checkExpiredDelaysAndResetAlarm() { synchronized (mLock) { - final long nowElapsedMillis = sElapsedRealtimeClock.millis(); long nextDelayTime = Long.MAX_VALUE; int nextDelayUid = 0; String nextDelayPackageName = null; @@ -284,7 +287,7 @@ public final class TimeController extends StateController { if (!job.hasTimingDelayConstraint()) { continue; } - if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) { + if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) { if (canStopTrackingJobLocked(job)) { it.remove(); } @@ -356,7 +359,11 @@ public final class TimeController extends StateController { * This alarm <b>will not</b> wake up the phone. */ private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { - alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); + // To avoid spamming AlarmManager in the case where many delay times are a few milliseconds + // apart, make sure the alarm is set no earlier than DELAY_COALESCE_TIME_MS since the last + // time a delay alarm went off and that the alarm is not scheduled for the past. + alarmTimeElapsedMillis = maybeAdjustAlarmTime(Math.max(alarmTimeElapsedMillis, + mLastFiredDelayExpiredElapsedMillis + DELAY_COALESCE_TIME_MS)); if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) { return; } @@ -416,6 +423,7 @@ public final class TimeController extends StateController { if (DEBUG) { Slog.d(TAG, "Delay-expired alarm fired"); } + mLastFiredDelayExpiredElapsedMillis = sElapsedRealtimeClock.millis(); checkExpiredDelaysAndResetAlarm(); } }; @@ -429,6 +437,9 @@ public final class TimeController extends StateController { pw.print("Next delay alarm in "); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); pw.println(); + pw.print("Last delay alarm fired @ "); + TimeUtils.formatDuration(nowElapsed, mLastFiredDelayExpiredElapsedMillis, pw); + pw.println(); pw.print("Next deadline alarm in "); TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw); pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 22e1eec8883d..6f7dde292c56 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -92,11 +92,11 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.IndentingPrintWriter; -import android.util.KeyValueListParser; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -117,8 +117,6 @@ import com.android.server.usage.AppIdleHistory.AppUsageHistory; import java.io.File; import java.io.PrintWriter; -import java.time.Duration; -import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -144,10 +142,11 @@ public class AppStandbyController implements AppStandbyInternal { private static final long ONE_DAY = ONE_HOUR * 24; /** - * The minimum amount of time the screen must have been on before an app can time out from its - * current bucket to the next bucket. + * The default minimum amount of time the screen must have been on before an app can time out + * from its current bucket to the next bucket. */ - private static final long[] SCREEN_TIME_THRESHOLDS = { + @VisibleForTesting + static final long[] DEFAULT_SCREEN_TIME_THRESHOLDS = { 0, 0, COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR, @@ -155,9 +154,10 @@ public class AppStandbyController implements AppStandbyInternal { COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR }; - /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */ - private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME - ? new long[SCREEN_TIME_THRESHOLDS.length] + /** The minimum allowed values for each index in {@link #DEFAULT_SCREEN_TIME_THRESHOLDS}. */ + @VisibleForTesting + static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME + ? new long[DEFAULT_SCREEN_TIME_THRESHOLDS.length] : new long[]{ 0, 0, @@ -167,20 +167,22 @@ public class AppStandbyController implements AppStandbyInternal { }; /** - * The minimum amount of elapsed time that must have passed before an app can time out from its - * current bucket to the next bucket. + * The default minimum amount of elapsed time that must have passed before an app can time out + * from its current bucket to the next bucket. */ - private static final long[] ELAPSED_TIME_THRESHOLDS = { + @VisibleForTesting + static final long[] DEFAULT_ELAPSED_TIME_THRESHOLDS = { 0, - COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR, - COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR, + COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR, + COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR, COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR, COMPRESS_TIME ? 32 * ONE_MINUTE : 30 * ONE_DAY }; - /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */ - private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME - ? new long[ELAPSED_TIME_THRESHOLDS.length] + /** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */ + @VisibleForTesting + static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME + ? new long[DEFAULT_ELAPSED_TIME_THRESHOLDS.length] : new long[]{ 0, ONE_HOUR, @@ -198,7 +200,8 @@ public class AppStandbyController implements AppStandbyInternal { }; /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */ - private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR; + private static final long DEFAULT_PREDICTION_TIMEOUT = + COMPRESS_TIME ? 10 * ONE_MINUTE : 12 * ONE_HOUR; /** * Indicates the maximum wait time for admin data to be available; @@ -269,58 +272,64 @@ public class AppStandbyController implements AppStandbyInternal { static final int MSG_REPORT_SYNC_SCHEDULED = 12; static final int MSG_REPORT_EXEMPTED_SYNC_START = 13; - long mCheckIdleIntervalMillis; + long mCheckIdleIntervalMillis = Math.min(DEFAULT_ELAPSED_TIME_THRESHOLDS[1] / 4, + ConstantsObserver.DEFAULT_CHECK_IDLE_INTERVAL_MS); /** * The minimum amount of time the screen must have been on before an app can time out from its * current bucket to the next bucket. */ - long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS; + long[] mAppStandbyScreenThresholds = DEFAULT_SCREEN_TIME_THRESHOLDS; /** * The minimum amount of elapsed time that must have passed before an app can time out from its * current bucket to the next bucket. */ - long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS; + long[] mAppStandbyElapsedThresholds = DEFAULT_ELAPSED_TIME_THRESHOLDS; /** Minimum time a strong usage event should keep the bucket elevated. */ - long mStrongUsageTimeoutMillis; + long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT; /** Minimum time a notification seen event should keep the bucket elevated. */ - long mNotificationSeenTimeoutMillis; + long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT; /** Minimum time a system update event should keep the buckets elevated. */ - long mSystemUpdateUsageTimeoutMillis; + long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT; /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */ - long mPredictionTimeoutMillis; + long mPredictionTimeoutMillis = DEFAULT_PREDICTION_TIMEOUT; /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */ - long mSyncAdapterTimeoutMillis; + long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_SYNC_ADAPTER_TIMEOUT; /** * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in * non-doze */ - long mExemptedSyncScheduledNonDozeTimeoutMillis; + long mExemptedSyncScheduledNonDozeTimeoutMillis = + ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT; /** * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in * doze */ - long mExemptedSyncScheduledDozeTimeoutMillis; + long mExemptedSyncScheduledDozeTimeoutMillis = + ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT; /** * Maximum time an exempted sync should keep the buckets elevated, when sync is started. */ - long mExemptedSyncStartTimeoutMillis; + long mExemptedSyncStartTimeoutMillis = ConstantsObserver.DEFAULT_EXEMPTED_SYNC_START_TIMEOUT; /** * Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled */ - long mUnexemptedSyncScheduledTimeoutMillis; + long mUnexemptedSyncScheduledTimeoutMillis = + ConstantsObserver.DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT; /** Maximum time a system interaction should keep the buckets elevated. */ - long mSystemInteractionTimeoutMillis; + long mSystemInteractionTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_INTERACTION_TIMEOUT; /** * Maximum time a foreground service start should keep the buckets elevated if the service * start is the first usage of the app */ - long mInitialForegroundServiceStartTimeoutMillis; + long mInitialForegroundServiceStartTimeoutMillis = + ConstantsObserver.DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT; /** * User usage that would elevate an app's standby bucket will also elevate the standby bucket of * cross profile connected apps. Explicit standby bucket setting via * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated. */ - boolean mLinkCrossProfileApps; + boolean mLinkCrossProfileApps = + ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS; /** * Whether we should allow apps into the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not. @@ -481,9 +490,8 @@ public class AppStandbyController implements AppStandbyInternal { if (phase == PHASE_SYSTEM_SERVICES_READY) { Slog.d(TAG, "Setting app idle enabled state"); // Observe changes to the threshold - SettingsObserver settingsObserver = new SettingsObserver(mHandler); - settingsObserver.registerObserver(); - settingsObserver.updateSettings(); + ConstantsObserver settingsObserver = new ConstantsObserver(mHandler); + settingsObserver.start(); mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class); @@ -1964,7 +1972,8 @@ public class AppStandbyController implements AppStandbyInternal { * The minimum amount of time required since the last user interaction before an app can be * automatically placed in the RESTRICTED bucket. */ - long mAutoRestrictedBucketDelayMs = ONE_DAY; + long mAutoRestrictedBucketDelayMs = + ConstantsObserver.DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS; /** * Cached set of apps that are power whitelisted, including those not whitelisted from idle. */ @@ -2136,9 +2145,9 @@ public class AppStandbyController implements AppStandbyInternal { return appWidgetManager.isBoundWidgetPackage(packageName, userId); } - String getAppIdleSettings() { - return Global.getString(mContext.getContentResolver(), - Global.APP_IDLE_CONSTANTS); + @NonNull + DeviceConfig.Properties getDeviceConfigProperties(String... keys) { + return DeviceConfig.getProperties(DeviceConfig.NAMESPACE_APP_STANDBY, keys); } /** Whether the device is in doze or not. */ @@ -2162,6 +2171,12 @@ public class AppStandbyController implements AppStandbyInternal { return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId); } + void registerDeviceConfigPropertiesChangedListener( + @NonNull DeviceConfig.OnPropertiesChangedListener listener) { + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_STANDBY, + JobSchedulerBackgroundThread.getExecutor(), listener); + } + void dump(PrintWriter pw) { pw.println("mPowerWhitelistedApps=["); synchronized (mPowerWhitelistedApps) { @@ -2286,11 +2301,11 @@ public class AppStandbyController implements AppStandbyInternal { }; /** - * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}. + * Observe changes for {@link DeviceConfig#NAMESPACE_APP_STANDBY} and other standby related + * Settings constants. */ - private class SettingsObserver extends ContentObserver { - private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds"; - private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds"; + private class ConstantsObserver extends ContentObserver implements + DeviceConfig.OnPropertiesChangedListener { private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration"; private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION = "notification_seen_duration"; @@ -2314,33 +2329,69 @@ public class AppStandbyController implements AppStandbyInternal { "auto_restricted_bucket_delay_ms"; private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = "cross_profile_apps_share_standby_buckets"; - public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR; - public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR; - public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR; - public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE; - public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE; - public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = 10 * ONE_MINUTE; - public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = 4 * ONE_HOUR; - public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE; - public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE; - public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE; - public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY; + private static final String KEY_PREFIX_SCREEN_TIME_THRESHOLD = "screen_threshold_"; + private final String[] KEYS_SCREEN_TIME_THRESHOLDS = { + KEY_PREFIX_SCREEN_TIME_THRESHOLD + "active", + KEY_PREFIX_SCREEN_TIME_THRESHOLD + "working_set", + KEY_PREFIX_SCREEN_TIME_THRESHOLD + "frequent", + KEY_PREFIX_SCREEN_TIME_THRESHOLD + "rare", + KEY_PREFIX_SCREEN_TIME_THRESHOLD + "restricted" + }; + private static final String KEY_PREFIX_ELAPSED_TIME_THRESHOLD = "elapsed_threshold_"; + private final String[] KEYS_ELAPSED_TIME_THRESHOLDS = { + KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "active", + KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "working_set", + KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "frequent", + KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare", + KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted" + }; + public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS = + COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; + public static final long DEFAULT_STRONG_USAGE_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR; + public static final long DEFAULT_NOTIFICATION_TIMEOUT = + COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR; + public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = + COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR; + public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; + public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; + public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = + COMPRESS_TIME ? (ONE_MINUTE / 2) : 10 * ONE_MINUTE; + public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; + public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; + public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; + public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 30 * ONE_MINUTE; + public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = + COMPRESS_TIME ? ONE_MINUTE : ONE_DAY; public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true; - private final KeyValueListParser mParser = new KeyValueListParser(','); - - SettingsObserver(Handler handler) { + ConstantsObserver(Handler handler) { super(handler); } - void registerObserver() { + public void start() { final ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this); + // APP_STANDBY_ENABLED is a SystemApi that some apps may be watching, so best to + // leave it in Settings. cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this); + // Leave ENABLE_RESTRICTED_BUCKET as a user-controlled setting which will stay in + // Settings. + // TODO: make setting user-specific cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET), false, this); + // ADAPTIVE_BATTERY_MANAGEMENT_ENABLED is a user setting, so it has to stay in Settings. cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED), false, this); + mInjector.registerDeviceConfigPropertiesChangedListener(this); + // Load all the constants. + onPropertiesChanged(mInjector.getDeviceConfigProperties()); + updateSettings(); } @Override @@ -2349,6 +2400,107 @@ public class AppStandbyController implements AppStandbyInternal { postOneTimeCheckIdleStates(); } + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + boolean timeThresholdsUpdated = false; + synchronized (mAppIdleLock) { + for (String name : properties.getKeyset()) { + if (name == null) { + continue; + } + switch (name) { + case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS: + mInjector.mAutoRestrictedBucketDelayMs = Math.max( + COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR, + properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS, + DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS)); + break; + case KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS: + mLinkCrossProfileApps = properties.getBoolean( + KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS, + DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS); + break; + case KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION: + mInitialForegroundServiceStartTimeoutMillis = properties.getLong( + KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION, + DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT); + break; + case KEY_NOTIFICATION_SEEN_HOLD_DURATION: + mNotificationSeenTimeoutMillis = properties.getLong( + KEY_NOTIFICATION_SEEN_HOLD_DURATION, + DEFAULT_NOTIFICATION_TIMEOUT); + break; + case KEY_STRONG_USAGE_HOLD_DURATION: + mStrongUsageTimeoutMillis = properties.getLong( + KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT); + break; + case KEY_PREDICTION_TIMEOUT: + mPredictionTimeoutMillis = properties.getLong( + KEY_PREDICTION_TIMEOUT, DEFAULT_PREDICTION_TIMEOUT); + break; + case KEY_SYSTEM_INTERACTION_HOLD_DURATION: + mSystemInteractionTimeoutMillis = properties.getLong( + KEY_SYSTEM_INTERACTION_HOLD_DURATION, + DEFAULT_SYSTEM_INTERACTION_TIMEOUT); + break; + case KEY_SYSTEM_UPDATE_HOLD_DURATION: + mSystemUpdateUsageTimeoutMillis = properties.getLong( + KEY_SYSTEM_UPDATE_HOLD_DURATION, DEFAULT_SYSTEM_UPDATE_TIMEOUT); + break; + case KEY_SYNC_ADAPTER_HOLD_DURATION: + mSyncAdapterTimeoutMillis = properties.getLong( + KEY_SYNC_ADAPTER_HOLD_DURATION, DEFAULT_SYNC_ADAPTER_TIMEOUT); + break; + case KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION: + mExemptedSyncScheduledDozeTimeoutMillis = properties.getLong( + KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION, + DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT); + break; + case KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION: + mExemptedSyncScheduledNonDozeTimeoutMillis = properties.getLong( + KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION, + DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT); + break; + case KEY_EXEMPTED_SYNC_START_HOLD_DURATION: + mExemptedSyncStartTimeoutMillis = properties.getLong( + KEY_EXEMPTED_SYNC_START_HOLD_DURATION, + DEFAULT_EXEMPTED_SYNC_START_TIMEOUT); + break; + case KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION: + mUnexemptedSyncScheduledTimeoutMillis = properties.getLong( + KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, + DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); + break; + default: + if (!timeThresholdsUpdated + && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD) + || name.startsWith(KEY_PREFIX_ELAPSED_TIME_THRESHOLD))) { + updateTimeThresholds(); + timeThresholdsUpdated = true; + } + break; + } + } + } + postOneTimeCheckIdleStates(); + } + + private void updateTimeThresholds() { + // Query the values as an atomic set. + final DeviceConfig.Properties screenThresholdProperties = + mInjector.getDeviceConfigProperties(KEYS_SCREEN_TIME_THRESHOLDS); + final DeviceConfig.Properties elapsedThresholdProperties = + mInjector.getDeviceConfigProperties(KEYS_ELAPSED_TIME_THRESHOLDS); + mAppStandbyScreenThresholds = generateThresholdArray( + screenThresholdProperties, KEYS_SCREEN_TIME_THRESHOLDS, + DEFAULT_SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS); + mAppStandbyElapsedThresholds = generateThresholdArray( + elapsedThresholdProperties, KEYS_ELAPSED_TIME_THRESHOLDS, + DEFAULT_ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS); + mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4, + DEFAULT_CHECK_IDLE_INTERVAL_MS); + } + void updateSettings() { if (DEBUG) { Slog.d(TAG, @@ -2357,126 +2509,43 @@ public class AppStandbyController implements AppStandbyInternal { Slog.d(TAG, "adaptivebat=" + Global.getString(mContext.getContentResolver(), Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED)); - Slog.d(TAG, "appidleconstants=" + Global.getString( - mContext.getContentResolver(), - Global.APP_IDLE_CONSTANTS)); - } - - // Look at global settings for this. - // TODO: Maybe apply different thresholds for different users. - try { - mParser.setString(mInjector.getAppIdleSettings()); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage()); - // fallthrough, mParser is empty and all defaults will be returned. } synchronized (mAppIdleLock) { - - String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null); - mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue, - SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS); - - String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, - null); - mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue, - ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS); - mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4, - COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours - mStrongUsageTimeoutMillis = mParser.getDurationMillis( - KEY_STRONG_USAGE_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STRONG_USAGE_TIMEOUT); - mNotificationSeenTimeoutMillis = mParser.getDurationMillis( - KEY_NOTIFICATION_SEEN_HOLD_DURATION, - COMPRESS_TIME ? 12 * ONE_MINUTE : DEFAULT_NOTIFICATION_TIMEOUT); - mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis( - KEY_SYSTEM_UPDATE_HOLD_DURATION, - COMPRESS_TIME ? 2 * ONE_MINUTE : DEFAULT_SYSTEM_UPDATE_TIMEOUT); - mPredictionTimeoutMillis = mParser.getDurationMillis( - KEY_PREDICTION_TIMEOUT, - COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT); - mSyncAdapterTimeoutMillis = mParser.getDurationMillis( - KEY_SYNC_ADAPTER_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT); - - mExemptedSyncScheduledNonDozeTimeoutMillis = mParser.getDurationMillis( - KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION, - COMPRESS_TIME ? (ONE_MINUTE / 2) - : DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT); - - mExemptedSyncScheduledDozeTimeoutMillis = mParser.getDurationMillis( - KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE - : DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT); - - mExemptedSyncStartTimeoutMillis = mParser.getDurationMillis( - KEY_EXEMPTED_SYNC_START_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE - : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT); - - mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis( - KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, - COMPRESS_TIME - ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); - - mSystemInteractionTimeoutMillis = mParser.getDurationMillis( - KEY_SYSTEM_INTERACTION_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT); - - mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis( - KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE : - DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT); - - mInjector.mAutoRestrictedBucketDelayMs = Math.max( - COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR, - mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS, - COMPRESS_TIME - ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS)); - - mLinkCrossProfileApps = mParser.getBoolean( - KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS, - DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS); - mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled(); } - // Check if app_idle_enabled has changed. Do this after getting the rest of the settings - // in case we need to change something based on the new values. setAppIdleEnabled(mInjector.isAppIdleEnabled()); } - long[] parseLongArray(String values, long[] defaults, long[] minValues) { - if (values == null) return defaults; - if (values.isEmpty()) { + long[] generateThresholdArray(@NonNull DeviceConfig.Properties properties, + @NonNull String[] keys, long[] defaults, long[] minValues) { + if (properties.getKeyset().isEmpty()) { // Reset to defaults return defaults; - } else { - String[] thresholds = values.split("/"); - if (thresholds.length == THRESHOLD_BUCKETS.length) { - if (minValues.length != THRESHOLD_BUCKETS.length) { - Slog.wtf(TAG, "minValues array is the wrong size"); - // Use zeroes as the minimums. - minValues = new long[THRESHOLD_BUCKETS.length]; - } - long[] array = new long[THRESHOLD_BUCKETS.length]; - for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) { - try { - if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) { - array[i] = Math.max(minValues[i], - Duration.parse(thresholds[i]).toMillis()); - } else { - array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i])); - } - } catch (NumberFormatException|DateTimeParseException e) { - return defaults; - } - } - return array; - } else { - return defaults; - } } + if (keys.length != THRESHOLD_BUCKETS.length) { + // This should only happen in development. + throw new IllegalStateException( + "# keys (" + keys.length + ") != # buckets (" + + THRESHOLD_BUCKETS.length + ")"); + } + if (defaults.length != THRESHOLD_BUCKETS.length) { + // This should only happen in development. + throw new IllegalStateException( + "# defaults (" + defaults.length + ") != # buckets (" + + THRESHOLD_BUCKETS.length + ")"); + } + if (minValues.length != THRESHOLD_BUCKETS.length) { + Slog.wtf(TAG, "minValues array is the wrong size"); + // Use zeroes as the minimums. + minValues = new long[THRESHOLD_BUCKETS.length]; + } + long[] array = new long[THRESHOLD_BUCKETS.length]; + for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) { + array[i] = Math.max(minValues[i], properties.getLong(keys[i], defaults[i])); + } + return array; } } } diff --git a/api/current.txt b/api/current.txt index 26d77ef4318e..9ff7cc255bf4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -40338,6 +40338,7 @@ package android.provider { field public static final String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; field public static final String AUTHORITY = "media"; field @NonNull public static final android.net.Uri AUTHORITY_URI; + field public static final String EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT = "android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT"; field public static final String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS"; field public static final String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; field public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion"; @@ -44150,13 +44151,13 @@ package android.service.notification { method public boolean canBubble(); method public boolean canShowBadge(); method public android.app.NotificationChannel getChannel(); + method @Nullable public android.content.pm.ShortcutInfo getConversationShortcutInfo(); method public int getImportance(); method public CharSequence getImportanceExplanation(); method public String getKey(); method public long getLastAudiblyAlertedMillis(); method public String getOverrideGroupKey(); method public int getRank(); - method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo(); method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions(); method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies(); method public int getSuppressedVisualEffects(); @@ -57227,8 +57228,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -57454,8 +57455,8 @@ package android.view.inputmethod { method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -57489,8 +57490,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 872f1f2c0591..8ca290fb1b09 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -185,6 +185,7 @@ package android.provider { public final class DeviceConfig { field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager"; + field public static final String NAMESPACE_APP_STANDBY = "app_standby"; field public static final String NAMESPACE_DEVICE_IDLE = "device_idle"; } diff --git a/api/system-current.txt b/api/system-current.txt index 6d92db45c874..0b95c6d5bd91 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -682,7 +682,7 @@ package android.app { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; @@ -2202,6 +2202,7 @@ package android.content.pm { method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String); method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo); method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean); + method public void setSystemAppState(@NonNull String, int); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean); method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int); method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); @@ -2280,11 +2281,16 @@ package android.content.pm { field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 + field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 field public static final int MODULE_APEX_NAME = 1; // 0x1 field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 field public static final int RESTRICTION_NONE = 0; // 0x0 + field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0 + field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1 + field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2 + field public static final int SYSTEM_APP_STATE_UNINSTALLED = 3; // 0x3 } public abstract static class PackageManager.DexModuleRegisterCallback { @@ -7578,8 +7584,11 @@ package android.net.wifi { field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0 field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11 + field @Deprecated public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; // 0x3ec field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9 field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0 + field @Deprecated public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; // 0x3eb + field @Deprecated public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; // 0x3ea field @Deprecated public boolean allowAutojoin; field @Deprecated public int carrierId; field @Deprecated public String creatorName; @@ -10032,6 +10041,7 @@ package android.service.autofill { public static final class Dataset.Builder { ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData); method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); } @@ -12036,7 +12046,8 @@ package android.telephony.data { method public int getMtuV6(); method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses(); method public int getProtocolType(); - method public int getSuggestedRetryTime(); + method public long getRetryIntervalMillis(); + method @Deprecated public int getSuggestedRetryTime(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1 @@ -12048,6 +12059,7 @@ package android.telephony.data { field public static final int LINK_STATUS_DORMANT = 1; // 0x1 field public static final int LINK_STATUS_INACTIVE = 0; // 0x0 field public static final int LINK_STATUS_UNKNOWN = -1; // 0xffffffff + field public static final int RETRY_INTERVAL_UNDEFINED = -1; // 0xffffffff } public static final class DataCallResponse.Builder { @@ -12066,7 +12078,8 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>); method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int); - method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryIntervalMillis(long); + method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); } public final class DataProfile implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 6164e776a79f..785463a92c45 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -131,6 +131,7 @@ package android.app { public class ActivityTaskManager { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>); method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context); + method public static int getMaxNumPictureInPictureActions(@NonNull android.content.Context); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void moveTaskToRootTask(int, int, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean moveTopActivityToPinnedRootTask(int, @NonNull android.graphics.Rect); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksInWindowingModes(@NonNull int[]); @@ -269,6 +270,7 @@ package android.app { method public void disallowAssistantAdjustment(String); method public android.content.ComponentName getEffectsSuppressor(); method public boolean matchesCallFilter(android.os.Bundle); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel); } @@ -286,6 +288,7 @@ package android.app { public class TaskInfo { method @NonNull public android.content.res.Configuration getConfiguration(); + method @Nullable public android.app.PictureInPictureParams getPictureInPictureParams(); method @NonNull public android.window.WindowContainerToken getToken(); } @@ -1556,6 +1559,19 @@ package android.service.autofill { method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions(); } + public final class Dataset implements android.os.Parcelable { + method @Nullable public android.content.IntentSender getAuthentication(); + method @Nullable public android.content.ClipData getFieldContent(); + method @Nullable public java.util.ArrayList<android.view.autofill.AutofillId> getFieldIds(); + method @Nullable public java.util.ArrayList<android.view.autofill.AutofillValue> getFieldValues(); + method @Nullable public String getId(); + method public boolean isEmpty(); + } + + public static final class Dataset.Builder { + method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData); + } + public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } @@ -2339,6 +2355,15 @@ package android.widget.inline { package android.window { + public final class DisplayAreaAppearedInfo implements android.os.Parcelable { + ctor public DisplayAreaAppearedInfo(@NonNull android.window.DisplayAreaInfo, @NonNull android.view.SurfaceControl); + method public int describeContents(); + method @NonNull public android.window.DisplayAreaInfo getDisplayAreaInfo(); + method @NonNull public android.view.SurfaceControl getLeash(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.window.DisplayAreaAppearedInfo> CREATOR; + } + public final class DisplayAreaInfo implements android.os.Parcelable { ctor public DisplayAreaInfo(@NonNull android.window.WindowContainerToken, int, int); method public int describeContents(); @@ -2354,7 +2379,7 @@ package android.window { ctor public DisplayAreaOrganizer(); method public void onDisplayAreaAppeared(@NonNull android.window.DisplayAreaInfo, @NonNull android.view.SurfaceControl); method public void onDisplayAreaVanished(@NonNull android.window.DisplayAreaInfo); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void registerOrganizer(int); + method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.DisplayAreaAppearedInfo> registerOrganizer(int); field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1 field public static final int FEATURE_ONE_HANDED = 3; // 0x3 field public static final int FEATURE_ROOT = 0; // 0x0 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3c55bce4fc06..2ac345d2560a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2883,11 +2883,7 @@ public class Activity extends ContextThemeWrapper * but will always be at least three. */ public int getMaxNumPictureInPictureActions() { - try { - return ActivityTaskManager.getService().getMaxNumPictureInPictureActions(mToken); - } catch (RemoteException e) { - return 0; - } + return ActivityTaskManager.getMaxNumPictureInPictureActions(this); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4c9d017dfc1a..ec287fe10634 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -503,8 +503,7 @@ public class ActivityManager { @UnsupportedAppUsage public static final int PROCESS_STATE_TOP = 2; - /** @hide Process is bound to a TOP app. This is ranked below SERVICE_LOCATION so that - * it doesn't get the capability of location access while-in-use. */ + /** @hide Process is bound to a TOP app. */ public static final int PROCESS_STATE_BOUND_TOP = 3; /** @hide Process is hosting a foreground service. */ diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index c9b009becac4..c7b90897c8e7 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -437,6 +437,12 @@ public class ActivityTaskManager { return currentUiModeSupportsErrorDialogs(config); } + /** @return max allowed number of actions in picture-in-picture mode. */ + public static int getMaxNumPictureInPictureActions(@NonNull Context context) { + return context.getResources().getInteger( + com.android.internal.R.integer.config_pictureInPictureMaxNumberOfActions); + } + /** * Information you can retrieve about a root task in the system. * @hide diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 8a03fcc33d51..bd5913efdecb 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -303,7 +303,6 @@ interface IActivityTaskManager { boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params); void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params); void requestPictureInPictureMode(in IBinder token); - int getMaxNumPictureInPictureActions(in IBinder token); IBinder getUriPermissionOwnerForActivity(in IBinder activityToken); /** diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index c052186bb974..d798f22a6af9 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -177,9 +177,9 @@ interface INotificationManager boolean isNotificationListenerAccessGranted(in ComponentName listener); boolean isNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId); boolean isNotificationAssistantAccessGranted(in ComponentName assistant); - void setNotificationListenerAccessGranted(in ComponentName listener, boolean enabled); + void setNotificationListenerAccessGranted(in ComponentName listener, boolean enabled, boolean userSet); void setNotificationAssistantAccessGranted(in ComponentName assistant, boolean enabled); - void setNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId, boolean enabled); + void setNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId, boolean enabled, boolean userSet); void setNotificationAssistantAccessGrantedForUser(in ComponentName assistant, int userId, boolean enabled); List<String> getEnabledNotificationListenerPackages(); List<ComponentName> getEnabledNotificationListeners(int userId); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 27cd78acb35f..12460ba2bd4b 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1565,6 +1565,12 @@ public class NotificationManager { } } + /** @hide */ + public void setNotificationListenerAccessGranted( + @NonNull ComponentName listener, boolean granted) { + setNotificationListenerAccessGranted(listener, granted, true); + } + /** * Grants/revokes Notification Listener access to the given component for current user. * To grant access for a particular user, obtain this service by using the {@link Context} @@ -1572,15 +1578,17 @@ public class NotificationManager { * * @param listener Name of component to grant/revoke access * @param granted Grant/revoke access + * @param userSet Whether the action was triggered explicitly by user * @hide */ @SystemApi + @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted( - @NonNull ComponentName listener, boolean granted) { + @NonNull ComponentName listener, boolean granted, boolean userSet) { INotificationManager service = getService(); try { - service.setNotificationListenerAccessGranted(listener, granted); + service.setNotificationListenerAccessGranted(listener, granted, userSet); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1591,7 +1599,7 @@ public class NotificationManager { boolean granted) { INotificationManager service = getService(); try { - service.setNotificationListenerAccessGrantedForUser(listener, userId, granted); + service.setNotificationListenerAccessGrantedForUser(listener, userId, granted, true); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 4b3bebe36c29..73777909d417 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -136,6 +136,8 @@ import android.net.lowpan.ILowpanManager; import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; +import android.net.vcn.IVcnManagementService; +import android.net.vcn.VcnManager; import android.net.wifi.WifiFrameworkInitializer; import android.net.wifi.nl80211.WifiNl80211Manager; import android.nfc.NfcManager; @@ -385,6 +387,14 @@ public final class SystemServiceRegistry { ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE)); }}); + registerService(Context.VCN_MANAGEMENT_SERVICE, VcnManager.class, + new CachedServiceFetcher<VcnManager>() { + @Override + public VcnManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.VCN_MANAGEMENT_SERVICE); + IVcnManagementService service = IVcnManagementService.Stub.asInterface(b); + return new VcnManager(ctx, service); + }}); registerService(Context.IPSEC_SERVICE, IpSecManager.class, new CachedServiceFetcher<IpSecManager>() { diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 8a3ae04a3589..45e9c49c5322 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -241,6 +241,13 @@ public class TaskInfo { } /** @hide */ + @Nullable + @TestApi + public PictureInPictureParams getPictureInPictureParams() { + return pictureInPictureParams; + } + + /** @hide */ public void addLaunchCookie(IBinder cookie) { if (cookie == null || launchCookies.contains(cookie)) return; launchCookies.add(cookie); diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java index d9ea7d237e51..c0c1aa1634ff 100644 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ b/core/java/android/bluetooth/le/ScanRecord.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothUuid; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.Log; @@ -196,7 +195,7 @@ public final class ScanRecord { * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static ScanRecord parseFromBytes(byte[] scanRecord) { if (scanRecord == null) { return null; diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 223005bbf30c..8170bf92ae1e 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -47,6 +47,7 @@ public final class AssociationRequest implements Parcelable { private final boolean mSingleDevice; private final List<DeviceFilter<?>> mDeviceFilters; + private String mCallingPackage; private AssociationRequest( boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) { @@ -58,6 +59,7 @@ public final class AssociationRequest implements Parcelable { this( in.readByte() != 0, in.readParcelableList(new ArrayList<>(), AssociationRequest.class.getClassLoader())); + setCallingPackage(in.readString()); } /** @hide */ @@ -73,32 +75,45 @@ public final class AssociationRequest implements Parcelable { return mDeviceFilters; } + /** @hide */ + public String getCallingPackage() { + return mCallingPackage; + } + + /** @hide */ + public void setCallingPackage(String pkg) { + mCallingPackage = pkg; + } + @Override public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AssociationRequest that = (AssociationRequest) o; - return mSingleDevice == that.mSingleDevice && - Objects.equals(mDeviceFilters, that.mDeviceFilters); + return mSingleDevice == that.mSingleDevice + && Objects.equals(mDeviceFilters, that.mDeviceFilters) + && Objects.equals(mCallingPackage, that.mCallingPackage); } @Override public int hashCode() { - return Objects.hash(mSingleDevice, mDeviceFilters); + return Objects.hash(mSingleDevice, mDeviceFilters, mCallingPackage); } @Override public String toString() { - return "AssociationRequest{" + - "mSingleDevice=" + mSingleDevice + - ", mDeviceFilters=" + mDeviceFilters + - '}'; + return "AssociationRequest{" + + "mSingleDevice=" + mSingleDevice + + ", mDeviceFilters=" + mDeviceFilters + + ", mCallingPackage=" + mCallingPackage + + '}'; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByte((byte) (mSingleDevice ? 1 : 0)); dest.writeParcelableList(mDeviceFilters, flags); + dest.writeString(mCallingPackage); } @Override diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 78eb8591a3e9..c4d98671a598 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -651,45 +651,57 @@ public class ClipData implements Parcelable { StringBuilder b = new StringBuilder(128); b.append("ClipData.Item { "); - toShortString(b); + toShortString(b, true); b.append(" }"); return b.toString(); } - /** @hide */ - public void toShortString(StringBuilder b) { + /** + * Appends this item to the given builder. + * @param redactContent If true, redacts common forms of PII; otherwise appends full + * details. + * @hide + */ + public void toShortString(StringBuilder b, boolean redactContent) { + boolean first = true; if (mHtmlText != null) { - b.append("H:"); - b.append(mHtmlText); - } else if (mText != null) { - b.append("T:"); - b.append(mText); - } else if (mUri != null) { - b.append("U:"); - b.append(mUri); - } else if (mIntent != null) { - b.append("I:"); - mIntent.toShortString(b, true, true, true, true); - } else { - b.append("NULL"); + first = false; + if (redactContent) { + b.append("H(").append(mHtmlText.length()).append(')'); + } else { + b.append("H:").append(mHtmlText); + } } - } - - /** @hide */ - public void toShortSummaryString(StringBuilder b) { - if (mHtmlText != null) { - b.append("HTML"); - } else if (mText != null) { - b.append("TEXT"); - } else if (mUri != null) { - b.append("U:"); - b.append(mUri); - } else if (mIntent != null) { + if (mText != null) { + if (!first) { + b.append(' '); + } + first = false; + if (redactContent) { + b.append("T(").append(mText.length()).append(')'); + } else { + b.append("T:").append(mText); + } + } + if (mUri != null) { + if (!first) { + b.append(' '); + } + first = false; + if (redactContent) { + b.append("U(").append(mUri.getScheme()).append(')'); + } else { + b.append("U:").append(mUri); + } + } + if (mIntent != null) { + if (!first) { + b.append(' '); + } + first = false; b.append("I:"); - mIntent.toShortString(b, true, true, true, true); - } else { - b.append("NULL"); + mIntent.toShortString(b, redactContent, true, true, true); } } @@ -1040,17 +1052,21 @@ public class ClipData implements Parcelable { StringBuilder b = new StringBuilder(128); b.append("ClipData { "); - toShortString(b); + toShortString(b, true); b.append(" }"); return b.toString(); } - /** @hide */ - public void toShortString(StringBuilder b) { + /** + * Appends this clip to the given builder. + * @param redactContent If true, redacts common forms of PII; otherwise appends full details. + * @hide + */ + public void toShortString(StringBuilder b, boolean redactContent) { boolean first; if (mClipDescription != null) { - first = !mClipDescription.toShortString(b); + first = !mClipDescription.toShortString(b, redactContent); } else { first = true; } @@ -1064,26 +1080,21 @@ public class ClipData implements Parcelable { b.append('x'); b.append(mIcon.getHeight()); } - for (int i=0; i<mItems.size(); i++) { + if (mItems.size() != 1) { if (!first) { b.append(' '); } first = false; - b.append('{'); - mItems.get(i).toShortString(b); - b.append('}'); + b.append(mItems.size()).append(" items:"); } - } - - /** @hide */ - public void toShortStringShortItems(StringBuilder b, boolean first) { - if (mItems.size() > 0) { + for (int i = 0; i < mItems.size(); i++) { if (!first) { b.append(' '); } - for (int i=0; i<mItems.size(); i++) { - b.append("{...}"); - } + first = false; + b.append('{'); + mItems.get(i).toShortString(b, redactContent); + b.append('}'); } } diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index f9e6308515cf..73becb1c0f1c 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -330,30 +330,45 @@ public class ClipDescription implements Parcelable { StringBuilder b = new StringBuilder(128); b.append("ClipDescription { "); - toShortString(b); + toShortString(b, true); b.append(" }"); return b.toString(); } - /** @hide */ - public boolean toShortString(StringBuilder b) { + /** + * Appends this description to the given builder. + * @param redactContent If true, redacts common forms of PII; otherwise appends full details. + * @hide + */ + public boolean toShortString(StringBuilder b, boolean redactContent) { boolean first = !toShortStringTypesOnly(b); if (mLabel != null) { if (!first) { b.append(' '); } first = false; - b.append('"'); - b.append(mLabel); - b.append('"'); + if (redactContent) { + b.append("hasLabel(").append(mLabel.length()).append(')'); + } else { + b.append('"').append(mLabel).append('"'); + } } if (mExtras != null) { if (!first) { b.append(' '); } first = false; - b.append(mExtras.toString()); + if (redactContent) { + if (mExtras.isParcelled()) { + // We don't want this toString function to trigger un-parcelling. + b.append("hasExtras"); + } else { + b.append("hasExtras(").append(mExtras.size()).append(')'); + } + } else { + b.append(mExtras.toString()); + } } if (mTimeStamp > 0) { if (!first) { diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 03adbc782eef..49248b51a5c7 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -624,6 +624,20 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override + public void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri, + RemoteCallback callback) { + final Bundle result = new Bundle(); + try { + result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + uncanonicalize(callingPkg, attributionTag, uri)); + } catch (Exception e) { + result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR, + new ParcelableException(e)); + } + callback.sendResult(result); + } + + @Override public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException { uri = validateIncomingUri(uri); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 7bc59013bcfe..7d121d56c86d 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -382,6 +382,16 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } + case UNCANONICALIZE_ASYNC_TRANSACTION: { + data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); + String featureId = data.readString(); + Uri uri = Uri.CREATOR.createFromParcel(data); + RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data); + uncanonicalizeAsync(callingPkg, featureId, uri, callback); + return true; + } + case REFRESH_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); @@ -875,6 +885,25 @@ final class ContentProviderProxy implements IContentProvider } @Override + /* oneway */ public void uncanonicalizeAsync(String callingPkg, @Nullable String featureId, + Uri uri, RemoteCallback callback) throws RemoteException { + Parcel data = Parcel.obtain(); + try { + data.writeInterfaceToken(IContentProvider.descriptor); + + data.writeString(callingPkg); + data.writeString(featureId); + uri.writeToParcel(data, 0); + callback.writeToParcel(data, 0); + + mRemote.transact(IContentProvider.UNCANONICALIZE_ASYNC_TRANSACTION, data, null, + Binder.FLAG_ONEWAY); + } finally { + data.recycle(); + } + } + + @Override public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle extras, ICancellationSignal signal) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 50092d17c692..422d3f7c6784 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1338,7 +1338,14 @@ public abstract class ContentResolver implements ContentInterface { } try { - return provider.uncanonicalize(mPackageName, mAttributionTag, url); + final UriResultListener resultListener = new UriResultListener(); + provider.uncanonicalizeAsync(mPackageName, mAttributionTag, url, + new RemoteCallback(resultListener)); + resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } + return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1d7a54c3021c..46d4f222a6b4 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3416,6 +3416,7 @@ public abstract class Context { VIBRATOR_SERVICE, //@hide: STATUS_BAR_SERVICE, CONNECTIVITY_SERVICE, + VCN_MANAGEMENT_SERVICE, //@hide: IP_MEMORY_STORE_SERVICE, IPSEC_SERVICE, VPN_MANAGEMENT_SERVICE, @@ -3995,6 +3996,16 @@ public abstract class Context { public static final String CONNECTIVITY_SERVICE = "connectivity"; /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager} + * for managing Virtual Carrier Networks + * + * @see #getSystemService(String) + * @see android.net.vcn.VcnManager + * @hide + */ + public static final String VCN_MANAGEMENT_SERVICE = "vcn_management"; + + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.net.INetd} for communicating with the network stack * @hide diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index becba67a0198..9210b132c75a 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -137,6 +137,14 @@ public interface IContentProvider extends IInterface { public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) throws RemoteException; + /** + * A oneway version of uncanonicalize. The functionality is exactly the same, except that the + * call returns immediately, and the resulting type is returned when available via + * a binder callback. + */ + void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri, + RemoteCallback callback) throws RemoteException; + public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url, @Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException; @@ -172,4 +180,5 @@ public interface IContentProvider extends IInterface { static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27; int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28; int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29; + int UNCANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 30; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 06ac5dbed702..782f0cd04dfc 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -10485,17 +10485,6 @@ public class Intent implements Parcelable, Cloneable { } /** @hide */ - public String toInsecureStringWithClip() { - StringBuilder b = new StringBuilder(128); - - b.append("Intent { "); - toShortString(b, false, true, true, true); - b.append(" }"); - - return b.toString(); - } - - /** @hide */ public String toShortString(boolean secure, boolean comp, boolean extras, boolean clip) { StringBuilder b = new StringBuilder(128); toShortString(b, secure, comp, extras, clip); @@ -10581,16 +10570,7 @@ public class Intent implements Parcelable, Cloneable { b.append(' '); } b.append("clip={"); - if (clip) { - mClipData.toShortString(b); - } else { - if (mClipData.getDescription() != null) { - first = !mClipData.getDescription().toShortStringTypesOnly(b); - } else { - first = true; - } - mClipData.toShortStringShortItems(b, first); - } + mClipData.toShortString(b, !clip || secure); first = false; b.append('}'); } @@ -10668,11 +10648,7 @@ public class Intent implements Parcelable, Cloneable { } if (mClipData != null) { StringBuilder b = new StringBuilder(); - if (clip) { - mClipData.toShortString(b); - } else { - mClipData.toShortStringShortItems(b, false); - } + mClipData.toShortString(b, !clip || secure); proto.write(IntentProto.CLIP_DATA, b.toString()); } if (extras && mExtras != null) { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index dfa9093301c3..c0f581717641 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -567,9 +567,11 @@ public abstract class PackageManager { public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO; /** - * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app. + * {@link PackageInfo} flag: include system apps that are in the uninstalled state and have + * been set to be hidden until installed via {@link #setSystemAppState}. * @hide */ + @SystemApi public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000; /** @@ -3768,27 +3770,34 @@ public abstract class PackageManager { public @interface SystemAppState {} /** - * Constant for noting system app state as hidden before installation + * Constant for use with {@link #setSystemAppState} to mark a system app as hidden until + * installation. * @hide */ + @SystemApi public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; /** - * Constant for noting system app state as visible before installation + * Constant for use with {@link #setSystemAppState} to mark a system app as not hidden until + * installation. * @hide */ + @SystemApi public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; /** - * Constant for noting system app state as installed + * Constant for use with {@link #setSystemAppState} to change a system app's state to installed. * @hide */ + @SystemApi public static final int SYSTEM_APP_STATE_INSTALLED = 2; /** - * Constant for noting system app state as not installed + * Constant for use with {@link #setSystemAppState} to change a system app's state to + * uninstalled. * @hide */ + @SystemApi public static final int SYSTEM_APP_STATE_UNINSTALLED = 3; /** @@ -7092,11 +7101,18 @@ public abstract class PackageManager { @NonNull UserHandle userHandle); /** - * Sets system app state + * Sets the state of a system app. + * + * This method can be used to change a system app's hidden-until-installed state (via + * {@link #SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN} and + * {@link #SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE} or its installation state (via + * {@link #SYSTEM_APP_STATE_INSTALLED} and {@link #SYSTEM_APP_STATE_UNINSTALLED}. + * * @param packageName Package name of the app. * @param state State of the app. * @hide */ + @SystemApi public void setSystemAppState(@NonNull String packageName, @SystemAppState int state) { throw new RuntimeException("Not implemented. Must override in a subclass"); } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 7ed803f78260..6a6be75c4606 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -1212,8 +1212,13 @@ public final class ShortcutInfo implements Parcelable { } /** - * Sets categories for a shortcut. Launcher apps may use this information to - * categorize shortcuts. + * Sets categories for a shortcut. + * <ul> + * <li>Launcher apps may use this information to categorize shortcuts + * <li> Used by the system to associate a published Sharing Shortcut with supported + * mimeTypes. Required for published Sharing Shortcuts with a matching category + * declared in share targets, defined in the app's manifest linked shortcuts xml file. + * </ul> * * @see #SHORTCUT_CATEGORY_CONVERSATION * @see ShortcutInfo#getCategories() diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 63397c05edc2..d2c74e963689 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -419,6 +419,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * Use the provided handler thread for events. + * @param handler + */ + private void useHandler(Handler handler) { + if (handler != null) { + mHandler = new MyHandler(handler.getLooper()); + } else if (mHandler.getLooper() != mContext.getMainLooper()) { + mHandler = new MyHandler(mContext.getMainLooper()); + } + } + + /** * Request authentication of a crypto object. This call warms up the fingerprint hardware * and starts scanning for a fingerprint. It terminates when * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or @@ -449,45 +461,15 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** - * Use the provided handler thread for events. - * @param handler - */ - private void useHandler(Handler handler) { - if (handler != null) { - mHandler = new MyHandler(handler.getLooper()); - } else if (mHandler.getLooper() != mContext.getMainLooper()){ - mHandler = new MyHandler(mContext.getMainLooper()); - } - } - - /** - * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal, - * AuthenticationCallback, Handler, int, Surface)} with {@code surface} set to null. - * - * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal, - * AuthenticationCallback, Handler, int, Surface) - * - * @hide - */ - @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) - public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - @NonNull AuthenticationCallback callback, Handler handler, int userId) { - authenticate(crypto, cancel, callback, handler, userId, null /* surface */); - } - - /** * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not * display the BiometricPrompt. * @param userId the user ID that the fingerprint hardware will authenticate for. - * @param surface for optical fingerprint sensors that require active illumination by the OLED - * display. Should be null for devices that don't require illumination. * @hide */ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - @NonNull AuthenticationCallback callback, Handler handler, int userId, - @Nullable Surface surface) { + @NonNull AuthenticationCallback callback, Handler handler, int userId) { if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -508,7 +490,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; mService.authenticate(mToken, operationId, userId, mServiceReceiver, - mContext.getOpPackageName(), surface); + mContext.getOpPackageName()); } catch (RemoteException e) { Slog.w(TAG, "Remote exception while authenticating: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try @@ -527,7 +509,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public void detectFingerprint(@NonNull CancellationSignal cancel, - @NonNull FingerprintDetectionCallback callback, int userId, @Nullable Surface surface) { + @NonNull FingerprintDetectionCallback callback, int userId) { if (mService == null) { return; } @@ -543,28 +525,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mService.detectFingerprint(mToken, userId, mServiceReceiver, - mContext.getOpPackageName(), surface); + mContext.getOpPackageName()); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); } } /** - * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int, - * EnrollmentCallback, Surface)} with {@code surface} set to null. - * - * @see FingerprintManager#enroll(byte[], CancellationSignal, int, EnrollmentCallback, - * Surface) - * - * @hide - */ - @RequiresPermission(MANAGE_FINGERPRINT) - public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, - EnrollmentCallback callback) { - enroll(hardwareAuthToken, cancel, userId, callback, null /* surface */); - } - - /** * Request fingerprint enrollment. This call warms up the fingerprint hardware * and starts scanning for fingerprints. Progress will be indicated by callbacks to the * {@link EnrollmentCallback} object. It terminates when @@ -581,7 +548,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, - EnrollmentCallback callback, @Nullable Surface surface) { + EnrollmentCallback callback) { if (userId == UserHandle.USER_CURRENT) { userId = getCurrentUserId(); } @@ -602,7 +569,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mEnrollmentCallback = callback; mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver, - mContext.getOpPackageName(), surface); + mContext.getOpPackageName()); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index 1f896cdf4298..9a9e47868b85 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -28,12 +28,41 @@ import android.os.Parcel; * @hide */ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInternal { + /** + * See {@link FingerprintSensorProperties.SensorType}. + */ public final @FingerprintSensorProperties.SensorType int sensorType; - // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT - // cannot be checked + + /** + * IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT + * cannot be checked + */ public final boolean resetLockoutRequiresHardwareAuthToken; /** + * The location of the center of the sensor if applicable. For example, sensors of type + * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the + * distance in pixels, measured from the left edge of the screen. + * TODO: Value should be provided from the HAL + */ + public final int sensorLocationX = 540; + + /** + * The location of the center of the sensor if applicable. For example, sensors of type + * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the + * distance in pixels, measured from the top edge of the screen. + * TODO: Value should be provided from the HAL + */ + public final int sensorLocationY = 1636; + + /** + * The radius of the sensor if applicable. For example, sensors of type + * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius + * of the sensor, in pixels. + */ + public final int sensorRadius = 130; + + /** * Initializes SensorProperties with specified values */ public FingerprintSensorPropertiesInternal(int sensorId, diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 95668372c5a1..547de9d3c86a 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -23,7 +23,6 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; -import android.view.Surface; import java.util.List; /** @@ -42,12 +41,12 @@ interface IFingerprintService { // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes // through FingerprintManager now. void authenticate(IBinder token, long operationId, int userId, - IFingerprintServiceReceiver receiver, String opPackageName, in Surface surface); + IFingerprintServiceReceiver receiver, String opPackageName); // Uses the fingerprint hardware to detect for the presence of a finger, without giving details // about accept/reject/lockout. void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver, - String opPackageName, in Surface surface); + String opPackageName); // This method prepares the service to start authenticating, but doesn't start authentication. // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be @@ -56,7 +55,7 @@ interface IFingerprintService { // startPreparedClient(). void prepareForAuthentication(IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie, - int callingUid, int callingPid, int callingUserId, in Surface surface); + int callingUid, int callingPid, int callingUserId); // Starts authentication with the previously prepared client. void startPreparedClient(int cookie); @@ -74,7 +73,7 @@ interface IFingerprintService { // Start fingerprint enrollment void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, - String opPackageName, in Surface surface); + String opPackageName); // Cancel enrollment in progress void cancelEnrollment(IBinder token); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index f75b88f8e24d..7fb73ceab4f4 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -231,7 +231,7 @@ public final class InputManager { * Waits for the event to be delivered to the application and handled. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171972397) public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = InputEventInjectionSync.WAIT_FOR_FINISHED; diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 0512305e71a2..bb2357e24617 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -280,7 +280,7 @@ class IInputMethodWrapper extends IInputMethod.Stub } CountDownLatch latch = new CountDownLatch(1); - mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP, + mCaller.getHandler().sendMessageAtFrontOfQueue(mCaller.obtainMessageOOOO(DO_DUMP, fd, fout, args, latch)); try { if (!latch.await(5, TimeUnit.SECONDS)) { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 070bec11b93a..d290465816b6 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -102,6 +102,7 @@ import android.view.ViewRootImpl; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets.Side; +import android.view.WindowInsets.Type; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.view.inputmethod.CompletionInfo; @@ -135,7 +136,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collections; +import java.util.ArrayList; import java.util.Objects; /** @@ -883,10 +884,19 @@ public class InputMethodService extends AbstractInputMethodService { /** Set region of the keyboard to be avoided from back gesture */ private void setImeExclusionRect(int visibleTopInsets) { - View inputFrameRootView = mInputFrame.getRootView(); - Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(), - inputFrameRootView.getHeight()); - inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r)); + View rootView = mInputFrame.getRootView(); + android.graphics.Insets systemGesture = + rootView.getRootWindowInsets().getInsets(Type.systemGestures()); + ArrayList<Rect> exclusionRects = new ArrayList<>(); + exclusionRects.add(new Rect(0, + visibleTopInsets, + systemGesture.left, + rootView.getHeight())); + exclusionRects.add(new Rect(rootView.getWidth() - systemGesture.right, + visibleTopInsets, + rootView.getWidth(), + rootView.getHeight())); + rootView.setSystemGestureExclusionRects(exclusionRects); } /** diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 327e42bdd2f7..44ebff99f3e9 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -353,10 +353,11 @@ public abstract class NetworkAgent { private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) { // The subtype can be changed with (TODO) setLegacySubtype, but it starts - // with the type and an empty description. + // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description. final NetworkInfo ni = new NetworkInfo(config.legacyType, 0, config.legacyTypeName, ""); ni.setIsAvailable(true); - ni.setExtraInfo(config.getLegacyExtraInfo()); + ni.setDetailedState(NetworkInfo.DetailedState.CONNECTING, null /* reason */, + config.getLegacyExtraInfo()); return ni; } diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING index abac81117deb..8c13ef98bedb 100644 --- a/core/java/android/net/TEST_MAPPING +++ b/core/java/android/net/TEST_MAPPING @@ -17,4 +17,4 @@ "path": "frameworks/opt/net/wifi" } ] -}
\ No newline at end of file +} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl index 0b279b882367..af06906ca2e9 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2020 The Android Open Source Project +/* + * Copyright 2020, 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. @@ -13,6 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.net; -@JavaOnlyStableParcelable parcelable TetheredClient;
\ No newline at end of file +package android.net.vcn; + +/** + * @hide + */ +interface IVcnManagementService { +} diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java new file mode 100644 index 000000000000..d563b0350187 --- /dev/null +++ b/core/java/android/net/vcn/VcnManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.SystemService; +import android.content.Context; + +/** + * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks + * + * @hide + */ +@SystemService(Context.VCN_MANAGEMENT_SERVICE) +public final class VcnManager { + @NonNull private static final String TAG = VcnManager.class.getSimpleName(); + + @NonNull private final Context mContext; + @NonNull private final IVcnManagementService mService; + + /** + * Construct an instance of VcnManager within an application context. + * + * @param ctx the application context for this manager + * @param service the VcnManagementService binder backing this manager + * + * @hide + */ + public VcnManager(@NonNull Context ctx, @NonNull IVcnManagementService service) { + mContext = requireNonNull(ctx, "missing context"); + mService = requireNonNull(service, "missing service"); + } +} diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index e738cb276d00..e1d900528f07 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -117,11 +117,6 @@ interface INetworkManagementService void removeRoute(int netId, in RouteInfo route); /** - * Set the specified MTU size - */ - void setMtu(String iface, int mtu); - - /** * Shuts down the service */ void shutdown(); @@ -350,28 +345,8 @@ interface INetworkManagementService */ boolean isNetworkActive(); - /** - * Add an interface to a network. - */ - void addInterfaceToNetwork(String iface, int netId); - - /** - * Remove an Interface from a network. - */ - void removeInterfaceFromNetwork(String iface, int netId); - void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid); - void setDefaultNetId(int netId); - void clearDefaultNetId(); - - /** - * Set permission for a network. - * @param permission PERMISSION_NONE to clear permissions. - * PERMISSION_NETWORK or PERMISSION_SYSTEM to set permission. - */ - void setNetworkPermission(int netId, int permission); - /** * Allow UID to call protect(). */ diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index aa2ad115a48f..8048b9df6097 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -725,7 +725,7 @@ public class Process { * Returns the identifier of this process' parent. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171962076) public static final int myPpid() { return Os.getppid(); } diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java index 1cd9c1a457a9..136e3de731a9 100644 --- a/core/java/android/os/SharedMemory.java +++ b/core/java/android/os/SharedMemory.java @@ -158,7 +158,7 @@ public final class SharedMemory implements Parcelable, Closeable { * * @hide Exposed for native ASharedMemory_dupFromJava() */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171971817) public int getFd() { return mFileDescriptor.getInt$(); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index af86a24caff4..2edd3227cf36 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -67,7 +67,7 @@ import java.util.Set; /** * Manages users and user details on a multi-user system. There are two major categories of - * users: fully customizable users with their own login, and managed profiles that share a workspace + * users: fully customizable users with their own login, and profiles that share a workspace * with a related user. * <p> * Users are different from accounts, which are managed by @@ -2298,7 +2298,7 @@ public class UserManager { * private app data storage is available. * <p>Requires {@code android.permission.MANAGE_USERS} or * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user} - * must be the calling user or a managed profile associated with it. + * must be the calling user or a profile associated with it. * * @param user to retrieve the unlocked state for. * @see Intent#ACTION_USER_UNLOCKED @@ -2496,7 +2496,7 @@ public class UserManager { * * <p>Requires {@code android.permission.MANAGE_USERS} or * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user} - * must be the calling user or a managed profile associated with it. + * must be the calling user or a profile associated with it. */ @RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_USERS, @@ -2620,7 +2620,7 @@ public class UserManager { * * <p>Requires {@code android.permission.MANAGE_USERS} or * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user} - * must be the calling user or a managed profile associated with it. + * must be the calling user or a profile associated with it. * * @hide */ @@ -3429,6 +3429,7 @@ public class UserManager { * Returns list of the profiles of userId including userId itself. * Note that this returns both enabled and not enabled profiles. See * {@link #getEnabledProfiles(int)} if you need only the enabled ones. + * <p>Note that this includes all profile types (not including Restricted profiles). * * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}. * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user. @@ -3481,6 +3482,7 @@ public class UserManager { /** * Returns list of the profiles of userId including userId itself. * Note that this returns only enabled. + * <p>Note that this includes all profile types (not including Restricted profiles). * * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}. * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user. @@ -3502,6 +3504,7 @@ public class UserManager { /** * Returns a list of UserHandles for profiles associated with the user that the calling process * is running on, including the user itself. + * <p>Note that this includes all profile types (not including Restricted profiles). * * @return A non-empty list of UserHandles associated with the calling user. */ @@ -3517,6 +3520,7 @@ public class UserManager { /** * Returns a list of ids for enabled profiles associated with the context user including the * user itself. + * <p>Note that this includes all profile types (not including Restricted profiles). * * @return A non-empty list of UserHandles associated with the calling user. * @hide @@ -3532,6 +3536,7 @@ public class UserManager { /** * Returns a list of ids for all profiles associated with the context user including the user * itself. + * <p>Note that this includes all profile types (not including Restricted profiles). * * @return A non-empty list of UserHandles associated with the calling user. * @hide @@ -3547,6 +3552,7 @@ public class UserManager { /** * Returns a list of ids for profiles associated with the context user including the user * itself. + * <p>Note that this includes all profile types (not including Restricted profiles). * * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles * @return A non-empty list of UserHandles associated with the calling user. @@ -3566,6 +3572,7 @@ public class UserManager { /** * Returns a list of ids for profiles associated with the specified user including the user * itself. + * <p>Note that this includes all profile types (not including Restricted profiles). * * @param userId id of the user to return profiles for * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles @@ -4355,8 +4362,9 @@ public class UserManager { } /** - * Returns creation time of the user or of a managed profile associated with the calling user. - * @param userHandle user handle of the user or a managed profile associated with the + * Returns creation time of the given user. The given user must be the calling user or + * a profile associated with it. + * @param userHandle user handle of the calling user or a profile associated with the * calling user. * @return creation time in milliseconds since Epoch time. */ diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 0315b561c120..0a4e867458e0 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -101,6 +101,14 @@ public final class DeviceConfig { public static final String NAMESPACE_APP_COMPAT = "app_compat"; /** + * Namespace for app standby configurations. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final String NAMESPACE_APP_STANDBY = "app_standby"; + + /** * Namespace for AttentionManagerService related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 21c4a11e57ac..780c4fa66d26 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6334,9 +6334,10 @@ public final class Settings { * Comma-separated list of location providers that are enabled. Do not rely on this value * being present or correct, or on ContentObserver notifications on the corresponding Uri. * - * @deprecated The preferred methods for checking provider status and listening for changes - * are via {@link LocationManager#isProviderEnabled(String)} and - * {@link LocationManager#PROVIDERS_CHANGED_ACTION}. + * @deprecated This setting no longer exists from Android S onwards as it no longer is + * capable of realistically reflecting location settings. Use {@link + * LocationManager#isProviderEnabled(String)} or {@link LocationManager#isLocationEnabled()} + * instead. */ @Deprecated public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed"; @@ -9054,12 +9055,6 @@ public final class Settings { public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; /** - * Controls if window magnification is enabled. - * @hide - */ - public static final String WINDOW_MAGNIFICATION = "window_magnification"; - - /** * Controls magnification mode when magnification is enabled via a system-wide triple tap * gesture or the accessibility shortcut. * @@ -11838,37 +11833,6 @@ public final class Settings { public static final String APP_TIME_LIMIT_USAGE_SOURCE = "app_time_limit_usage_source"; /** - * App standby (app idle) specific settings. - * This is encoded as a key=value list, separated by commas. Ex: - * <p> - * "idle_duration=5000,prediction_timeout=4500,screen_thresholds=0/0/60000/120000" - * <p> - * All durations are in millis. - * Array values are separated by forward slashes - * The following keys are supported: - * - * <pre> - * screen_thresholds (long[4]) - * elapsed_thresholds (long[4]) - * strong_usage_duration (long) - * notification_seen_duration (long) - * system_update_usage_duration (long) - * prediction_timeout (long) - * sync_adapter_duration (long) - * exempted_sync_duration (long) - * system_interaction_duration (long) - * initial_foreground_service_start_duration (long) - * cross_profile_apps_share_standby_buckets (boolean) - * </pre> - * - * <p> - * Type: string - * @hide - * @see com.android.server.usage.AppStandbyController - */ - public static final String APP_IDLE_CONSTANTS = "app_idle_constants"; - - /** * Enable ART bytecode verification verifications for debuggable apps. * 0 = disable, 1 = enable. * @hide @@ -14533,6 +14497,15 @@ public final class Settings { public static final String SHOW_NEW_LOCKSCREEN = "show_new_lockscreen"; /** + * Whether to show new notification dismissal. + * Values are: + * 0: Disabled + * 1: Enabled + * @hide + */ + public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss"; + + /** * Block untrusted touches mode. * * Can be one of: diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index cc3e57859b64..d859b1c33c94 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -751,7 +751,7 @@ public class RecoveryController { InternalRecoveryServiceException wrapUnexpectedServiceSpecificException( ServiceSpecificException e) { if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) { - return new InternalRecoveryServiceException(e.getMessage()); + return new InternalRecoveryServiceException(e.getMessage(), e); } // Should never happen. If it does, it's a bug, and we need to update how the method that diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 18d79927388b..8ae1b6bf702d 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -20,7 +20,10 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.content.ClipData; import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; @@ -97,7 +100,6 @@ import java.util.regex.Pattern; * with the lower case value of the view's text are shown. * <li>All other datasets are hidden. * </ol> - * */ public final class Dataset implements Parcelable { @@ -106,6 +108,7 @@ public final class Dataset implements Parcelable { private final ArrayList<RemoteViews> mFieldPresentations; private final ArrayList<InlinePresentation> mFieldInlinePresentations; private final ArrayList<DatasetFieldFilter> mFieldFilters; + @Nullable private final ClipData mFieldContent; private final RemoteViews mPresentation; @Nullable private final InlinePresentation mInlinePresentation; private final IntentSender mAuthentication; @@ -117,6 +120,7 @@ public final class Dataset implements Parcelable { mFieldPresentations = builder.mFieldPresentations; mFieldInlinePresentations = builder.mFieldInlinePresentations; mFieldFilters = builder.mFieldFilters; + mFieldContent = builder.mFieldContent; mPresentation = builder.mPresentation; mInlinePresentation = builder.mInlinePresentation; mAuthentication = builder.mAuthentication; @@ -124,11 +128,15 @@ public final class Dataset implements Parcelable { } /** @hide */ + @TestApi + @SuppressLint("ConcreteCollection") public @Nullable ArrayList<AutofillId> getFieldIds() { return mFieldIds; } /** @hide */ + @TestApi + @SuppressLint("ConcreteCollection") public @Nullable ArrayList<AutofillValue> getFieldValues() { return mFieldValues; } @@ -140,24 +148,37 @@ public final class Dataset implements Parcelable { } /** @hide */ - @Nullable - public InlinePresentation getFieldInlinePresentation(int index) { + public @Nullable InlinePresentation getFieldInlinePresentation(int index) { final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index); return inlinePresentation != null ? inlinePresentation : mInlinePresentation; } /** @hide */ - @Nullable - public DatasetFieldFilter getFilter(int index) { + public @Nullable DatasetFieldFilter getFilter(int index) { return mFieldFilters.get(index); } + /** + * Returns the content to be filled for a non-text suggestion. This is only applicable to + * augmented autofill. The target field for the content is available via {@link #getFieldIds()} + * (guaranteed to have a single field id set when the return value here is non-null). See + * {@link Builder#setContent(AutofillId, ClipData)} for more info. + * + * @hide + */ + @TestApi + public @Nullable ClipData getFieldContent() { + return mFieldContent; + } + /** @hide */ + @TestApi public @Nullable IntentSender getAuthentication() { return mAuthentication; } /** @hide */ + @TestApi public boolean isEmpty() { return mFieldIds == null || mFieldIds.isEmpty(); } @@ -179,6 +200,9 @@ public final class Dataset implements Parcelable { if (mFieldValues != null) { builder.append(", fieldValues=").append(mFieldValues); } + if (mFieldContent != null) { + builder.append(", fieldContent=").append(mFieldContent); + } if (mFieldPresentations != null) { builder.append(", fieldPresentations=").append(mFieldPresentations.size()); } @@ -207,7 +231,8 @@ public final class Dataset implements Parcelable { * * @hide */ - public String getId() { + @TestApi + public @Nullable String getId() { return mId; } @@ -221,6 +246,7 @@ public final class Dataset implements Parcelable { private ArrayList<RemoteViews> mFieldPresentations; private ArrayList<InlinePresentation> mFieldInlinePresentations; private ArrayList<DatasetFieldFilter> mFieldFilters; + @Nullable private ClipData mFieldContent; private RemoteViews mPresentation; @Nullable private InlinePresentation mInlinePresentation; private IntentSender mAuthentication; @@ -366,6 +392,36 @@ public final class Dataset implements Parcelable { } /** + * Sets the content for a field. + * + * <p>Only called by augmented autofill. + * + * <p>For a given field, either a {@link AutofillValue value} or content can be filled, but + * not both. Furthermore, when filling content, only a single field can be filled. + * + * @param id id returned by + * {@link android.app.assist.AssistStructure.ViewNode#getAutofillId()}. + * @param content content to be autofilled. Pass {@code null} if you do not have the content + * but the target view is a logical part of the dataset. For example, if the dataset needs + * authentication. + * + * @throws IllegalStateException if {@link #build()} was already called. + * + * @return this builder. + * + * @hide + */ + @TestApi + @SystemApi + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder setContent(@NonNull AutofillId id, @Nullable ClipData content) { + throwIfDestroyed(); + setLifeTheUniverseAndEverything(id, null, null, null, null); + mFieldContent = content; + return this; + } + + /** * Sets the value of a field. * * <b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, this method would @@ -659,6 +715,15 @@ public final class Dataset implements Parcelable { if (mFieldIds == null) { throw new IllegalStateException("at least one value must be set"); } + if (mFieldContent != null) { + if (mFieldIds.size() > 1) { + throw new IllegalStateException( + "when filling content, only one field can be filled"); + } + if (mFieldValues.get(0) != null) { + throw new IllegalStateException("cannot fill both content and values"); + } + } return new Dataset(this); } @@ -687,6 +752,7 @@ public final class Dataset implements Parcelable { parcel.writeTypedList(mFieldPresentations, flags); parcel.writeTypedList(mFieldInlinePresentations, flags); parcel.writeTypedList(mFieldFilters, flags); + parcel.writeParcelable(mFieldContent, flags); parcel.writeParcelable(mAuthentication, flags); parcel.writeString(mId); } @@ -694,18 +760,8 @@ public final class Dataset implements Parcelable { public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() { @Override public Dataset createFromParcel(Parcel parcel) { - // Always go through the builder to ensure the data ingested by - // the system obeys the contract of the builder to avoid attacks - // using specially crafted parcels. final RemoteViews presentation = parcel.readParcelable(null); final InlinePresentation inlinePresentation = parcel.readParcelable(null); - final Builder builder = presentation != null - ? inlinePresentation == null - ? new Builder(presentation) - : new Builder(presentation).setInlinePresentation(inlinePresentation) - : inlinePresentation == null - ? new Builder() - : new Builder(inlinePresentation); final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); final ArrayList<AutofillValue> values = @@ -716,6 +772,21 @@ public final class Dataset implements Parcelable { parcel.createTypedArrayList(InlinePresentation.CREATOR); final ArrayList<DatasetFieldFilter> filters = parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); + final ClipData fieldContent = parcel.readParcelable(null); + final IntentSender authentication = parcel.readParcelable(null); + final String datasetId = parcel.readString(); + + // Always go through the builder to ensure the data ingested by + // the system obeys the contract of the builder to avoid attacks + // using specially crafted parcels. + final Builder builder = (presentation != null) ? new Builder(presentation) + : new Builder(); + if (inlinePresentation != null) { + builder.setInlinePresentation(inlinePresentation); + } + if (fieldContent != null) { + builder.setContent(ids.get(0), fieldContent); + } final int inlinePresentationsSize = inlinePresentations.size(); for (int i = 0; i < ids.size(); i++) { final AutofillId id = ids.get(i); @@ -727,8 +798,8 @@ public final class Dataset implements Parcelable { builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, fieldInlinePresentation, filter); } - builder.setAuthentication(parcel.readParcelable(null)); - builder.setId(parcel.readString()); + builder.setAuthentication(authentication); + builder.setId(datasetId); return builder.build(); } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 89e27ba21443..aa9e289345db 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1885,7 +1885,7 @@ public abstract class NotificationListenerService extends Service { * <p>This might be null even if the notification is a conversation notification, if * the posting app hasn't opted into the full conversation feature set yet.</p> */ - public @Nullable ShortcutInfo getShortcutInfo() { + public @Nullable ShortcutInfo getConversationShortcutInfo() { return mShortcutInfo; } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 169507ce9871..787a81bac3c0 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -287,7 +287,7 @@ public class ZenModeConfig implements Parcelable { } StringBuilder buffer = new StringBuilder(automaticRules.size() * 28); - buffer.append('{'); + buffer.append("{\n"); for (int i = 0; i < automaticRules.size(); i++) { if (i > 0) { buffer.append(",\n"); @@ -1831,12 +1831,13 @@ public class ZenModeConfig implements Parcelable { public String toString() { return new StringBuilder(ZenRule.class.getSimpleName()).append('[') .append("id=").append(id) + .append(",state=").append(condition == null ? "STATE_FALSE" + : Condition.stateToString(condition.state)) .append(",enabled=").append(String.valueOf(enabled).toUpperCase()) .append(",snoozing=").append(snoozing) .append(",name=").append(name) .append(",zenMode=").append(Global.zenModeToString(zenMode)) .append(",conditionId=").append(conditionId) - .append(",condition=").append(condition) .append(",pkg=").append(pkg) .append(",component=").append(component) .append(",configActivity=").append(configurationActivity) @@ -1844,6 +1845,7 @@ public class ZenModeConfig implements Parcelable { .append(",enabler=").append(enabler) .append(",zenPolicy=").append(zenPolicy) .append(",modified=").append(modified) + .append(",condition=").append(condition) .append(']').toString(); } @@ -2011,6 +2013,10 @@ public class ZenModeConfig implements Parcelable { public Diff addLine(String item, Object from, Object to) { return addLine(item, from + "->" + to); } + + public boolean isEmpty() { + return lines.isEmpty(); + } } /** diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java index 9443ccfc7553..24748222b3af 100644 --- a/core/java/android/view/NotificationTopLineView.java +++ b/core/java/android/view/NotificationTopLineView.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.RemoteViews; @@ -36,6 +37,7 @@ import java.util.List; */ @RemoteViews.RemoteView public class NotificationTopLineView extends ViewGroup { + private final int mGravityY; private final int mChildMinWidth; private final int mContentEndMargin; private View mAppName; @@ -48,6 +50,9 @@ public class NotificationTopLineView extends ViewGroup { private int mHeaderTextMarginEnd; private List<View> mIconsAtEnd; + private int mMaxAscent; + private int mMaxDescent; + public NotificationTopLineView(Context context) { this(context, null); } @@ -67,6 +72,20 @@ public class NotificationTopLineView extends ViewGroup { Resources res = getResources(); mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width); mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end); + + // NOTE: Implementation only supports TOP, BOTTOM, and CENTER_VERTICAL gravities, + // with CENTER_VERTICAL being the default. + int[] attrIds = {android.R.attr.gravity}; + TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes); + int gravity = ta.getInt(0, 0); + ta.recycle(); + if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { + mGravityY = Gravity.BOTTOM; + } else if ((gravity & Gravity.TOP) == Gravity.TOP) { + mGravityY = Gravity.TOP; + } else { + mGravityY = Gravity.CENTER_VERTICAL; + } } @Override @@ -84,12 +103,16 @@ public class NotificationTopLineView extends ViewGroup { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int givenWidth = MeasureSpec.getSize(widthMeasureSpec); final int givenHeight = MeasureSpec.getSize(heightMeasureSpec); + final boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST; int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth, MeasureSpec.AT_MOST); int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight, MeasureSpec.AT_MOST); int totalWidth = getPaddingStart(); int iconWidth = getPaddingEnd(); + int maxChildHeight = -1; + mMaxAscent = -1; + mMaxDescent = -1; for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); if (child.getVisibility() == GONE) { @@ -108,6 +131,13 @@ public class NotificationTopLineView extends ViewGroup { } else { totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); } + int childBaseline = child.getBaseline(); + int childHeight = child.getMeasuredHeight(); + if (childBaseline != -1) { + mMaxAscent = Math.max(mMaxAscent, childBaseline); + mMaxDescent = Math.max(mMaxDescent, childHeight - childBaseline); + } + maxChildHeight = Math.max(maxChildHeight, childHeight); } // Ensure that there is at least enough space for the icons @@ -125,7 +155,7 @@ public class NotificationTopLineView extends ViewGroup { shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText, 0); } - setMeasuredDimension(givenWidth, givenHeight); + setMeasuredDimension(givenWidth, wrapHeight ? maxChildHeight : givenHeight); } private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView, @@ -146,7 +176,13 @@ public class NotificationTopLineView extends ViewGroup { int left = getPaddingStart(); int end = getMeasuredWidth(); int childCount = getChildCount(); - int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); + int ownHeight = b - t; + int childSpace = ownHeight - mPaddingTop - mPaddingBottom; + + // Instead of centering the baseline, pick a baseline that centers views which align to it. + // Only used when mGravityY is CENTER_VERTICAL + int baselineY = mPaddingTop + ((childSpace - (mMaxAscent + mMaxDescent)) / 2) + mMaxAscent; + for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child.getVisibility() == GONE) { @@ -156,8 +192,42 @@ public class NotificationTopLineView extends ViewGroup { MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams(); int layoutLeft; int layoutRight; - int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f); - int bottom = top + childHeight; + + // Calculate vertical alignment of the views, accounting for the view baselines + int childTop; + int childBaseline = child.getBaseline(); + switch (mGravityY) { + case Gravity.TOP: + childTop = mPaddingTop + params.topMargin; + if (childBaseline != -1) { + childTop += mMaxAscent - childBaseline; + } + break; + case Gravity.CENTER_VERTICAL: + if (childBaseline != -1) { + // Align baselines vertically only if the child is smaller than us + if (childSpace - childHeight > 0) { + childTop = baselineY - childBaseline; + } else { + childTop = mPaddingTop + (childSpace - childHeight) / 2; + } + } else { + childTop = mPaddingTop + ((childSpace - childHeight) / 2) + + params.topMargin - params.bottomMargin; + } + break; + case Gravity.BOTTOM: + int childBottom = ownHeight - mPaddingBottom; + childTop = childBottom - childHeight - params.bottomMargin; + if (childBaseline != -1) { + int descent = childHeight - childBaseline; + childTop -= (mMaxDescent - descent); + } + break; + default: + childTop = mPaddingTop; + } + // Icons that should go at the end if (mIconsAtEnd.contains(child)) { if (end == getMeasuredWidth()) { @@ -179,7 +249,7 @@ public class NotificationTopLineView extends ViewGroup { layoutLeft = getWidth() - layoutRight; layoutRight = getWidth() - ltrLeft; } - child.layout(layoutLeft, top, layoutRight, bottom); + child.layout(layoutLeft, childTop, layoutRight, childTop + childHeight); } updateTouchListener(); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 5a4584387dba..3a15aa26e357 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -653,12 +653,14 @@ public final class SurfaceControl implements Parcelable { private final Rect mSourceCrop = new Rect(); private final float mFrameScale; private final boolean mCaptureSecureLayers; + private final boolean mAllowProtected; private CaptureArgs(Builder<? extends Builder<?>> builder) { mPixelFormat = builder.mPixelFormat; mSourceCrop.set(builder.mSourceCrop); mFrameScale = builder.mFrameScale; mCaptureSecureLayers = builder.mCaptureSecureLayers; + mAllowProtected = builder.mAllowProtected; } /** @@ -671,6 +673,7 @@ public final class SurfaceControl implements Parcelable { private final Rect mSourceCrop = new Rect(); private float mFrameScale = 1; private boolean mCaptureSecureLayers; + private boolean mAllowProtected; /** * The desired pixel format of the returned buffer. @@ -709,6 +712,17 @@ public final class SurfaceControl implements Parcelable { } /** + * Whether to allow the screenshot of protected (DRM) content. Warning: The screenshot + * cannot be read in unprotected space. + * + * @see HardwareBuffer#USAGE_PROTECTED_CONTENT + */ + public T setAllowProtected(boolean allowProtected) { + mAllowProtected = allowProtected; + return getThis(); + } + + /** * Each sub class should return itself to allow the builder to chain properly */ abstract T getThis(); @@ -1907,16 +1921,23 @@ public final class SurfaceControl implements Parcelable { public float appRequestRefreshRateMin; public float appRequestRefreshRateMax; + /** + * If true this will allow switching between modes in different display configuration + * groups. This way the user may see visual interruptions when the display mode changes. + */ + public boolean allowGroupSwitching; + public DesiredDisplayConfigSpecs() {} public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) { copyFrom(other); } - public DesiredDisplayConfigSpecs(int defaultConfig, float primaryRefreshRateMin, - float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { + public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, + float appRequestRefreshRateMin, float appRequestRefreshRateMax) { this.defaultConfig = defaultConfig; + this.allowGroupSwitching = allowGroupSwitching; this.primaryRefreshRateMin = primaryRefreshRateMin; this.primaryRefreshRateMax = primaryRefreshRateMax; this.appRequestRefreshRateMin = appRequestRefreshRateMin; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index fb66b5298839..81db62857c17 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -19,6 +19,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; +import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; @@ -32,6 +33,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.AutofillOptions; +import android.content.ClipData; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -60,6 +62,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Choreographer; import android.view.KeyEvent; +import android.view.OnReceiveContentCallback; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -2350,6 +2353,49 @@ public final class AutofillManager { } } + private void autofillContent(int sessionId, AutofillId id, ClipData clip) { + synchronized (mLock) { + if (sessionId != mSessionId) { + return; + } + final AutofillClient client = getClient(); + if (client == null) { + return; + } + final View view = client.autofillClientFindViewByAutofillIdTraversal(id); + if (view == null) { + // Most likely view has been removed after the initial request was sent to the + // the service; this is fine, but we need to update the view status in the + // server side so it can be triggered again. + Log.d(TAG, "autofillContent(): no view with id " + id); + reportAutofillContentFailure(id); + return; + } + OnReceiveContentCallback.Payload payload = + new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL) + .build(); + boolean handled = view.onReceiveContent(payload); + if (!handled) { + Log.w(TAG, "autofillContent(): receiver returned false: id=" + id + + ", view=" + view + ", clip=" + clip); + reportAutofillContentFailure(id); + return; + } + mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, 1) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, 1)); + } + } + + private void reportAutofillContentFailure(AutofillId id) { + try { + mService.setAutofillFailure(mSessionId, Collections.singletonList(id), + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private LogMaker newLog(int category) { final LogMaker log = new LogMaker(category) .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId); @@ -3391,6 +3437,14 @@ public final class AutofillManager { } @Override + public void autofillContent(int sessionId, AutofillId id, ClipData content) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.autofillContent(sessionId, id, content)); + } + } + + @Override public void authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline) { final AutofillManager afm = mAfm.get(); diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index f8ccea5d8356..1f833f66c257 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -18,6 +18,7 @@ package android.view.autofill; import java.util.List; +import android.content.ClipData; import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; @@ -48,6 +49,11 @@ oneway interface IAutoFillManagerClient { boolean hideHighlight); /** + * Autofills the activity with rich content data (e.g. an image) from a dataset. + */ + void autofillContent(int sessionId, in AutofillId id, in ClipData content); + + /** * Authenticates a fill response or a data set. */ void authenticate(int sessionId, int authenticationId, in IntentSender intent, diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 093dfb4e196e..62b1b1f8cf53 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -502,7 +502,10 @@ public class BaseInputConnection implements InputConnection { * The default implementation returns the given amount of text from the * current cursor position in the buffer. */ - public CharSequence getTextBeforeCursor(int length, int flags) { + @Nullable + public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0) return null; + final Editable content = getEditable(); if (content == null) return null; @@ -558,7 +561,10 @@ public class BaseInputConnection implements InputConnection { * The default implementation returns the given amount of text from the * current cursor position in the buffer. */ - public CharSequence getTextAfterCursor(int length, int flags) { + @Nullable + public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0) return null; + final Editable content = getEditable(); if (content == null) return null; @@ -594,6 +600,8 @@ public class BaseInputConnection implements InputConnection { @Nullable public SurroundingText getSurroundingText( @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) { + if (beforeLength < 0 || afterLength < 0) return null; + final Editable content = getEditable(); if (content == null) return null; diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index c7acd298cd20..0fe47b7828d9 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -27,6 +27,8 @@ import android.text.TextUtils; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import com.android.internal.util.Preconditions; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -186,13 +188,15 @@ public interface InputConnection { * the current line, and specifically do not return 0 characters unless * the cursor is really at the start of the text.</p> * - * @param n The expected length of the text. + * @param n The expected length of the text. This must be non-negative. * @param flags Supplies additional options controlling how the text is * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * @return the text before the cursor position; the length of the * returned text might be less than <var>n</var>. + * @throws IllegalArgumentException if {@code n} is negative. */ - CharSequence getTextBeforeCursor(int n, int flags); + @Nullable + CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags); /** * Get <var>n</var> characters of text after the current cursor @@ -228,14 +232,16 @@ public interface InputConnection { * the current line, and specifically do not return 0 characters unless * the cursor is really at the end of the text.</p> * - * @param n The expected length of the text. + * @param n The expected length of the text. This must be non-negative. * @param flags Supplies additional options controlling how the text is * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * * @return the text after the cursor position; the length of the * returned text might be less than <var>n</var>. + * @throws IllegalArgumentException if {@code n} is negative. */ - CharSequence getTextAfterCursor(int n, int flags); + @Nullable + CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags); /** * Gets the selected text, if any. @@ -307,11 +313,15 @@ public interface InputConnection { * editor can't comply with the request for some reason, or the application does not implement * this method. The length of the returned text might be less than the sum of * <var>beforeLength</var> and <var>afterLength</var> . + * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative. */ @Nullable default SurroundingText getSurroundingText( @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, @GetTextType int flags) { + Preconditions.checkArgumentNonnegative(beforeLength); + Preconditions.checkArgumentNonnegative(afterLength); + CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags); if (textBeforeCursor == null) { textBeforeCursor = ""; diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index ec7fa60a62ba..77956d15e399 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -16,11 +16,14 @@ package android.view.inputmethod; +import android.annotation.IntRange; import android.annotation.Nullable; import android.os.Bundle; import android.os.Handler; import android.view.KeyEvent; +import com.android.internal.util.Preconditions; + /** * <p>Wrapper class for proxying calls to another InputConnection. Subclass and have fun! */ @@ -74,18 +77,24 @@ public class InputConnectionWrapper implements InputConnection { /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. + * @throws IllegalArgumentException if {@code length} is negative. */ + @Nullable @Override - public CharSequence getTextBeforeCursor(int n, int flags) { + public CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags) { + Preconditions.checkArgumentNonnegative(n); return mTarget.getTextBeforeCursor(n, flags); } /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. + * @throws IllegalArgumentException if {@code length} is negative. */ + @Nullable @Override - public CharSequence getTextAfterCursor(int n, int flags) { + public CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags) { + Preconditions.checkArgumentNonnegative(n); return mTarget.getTextAfterCursor(n, flags); } @@ -101,10 +110,13 @@ public class InputConnectionWrapper implements InputConnection { /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. + * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative. */ @Nullable @Override public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) { + Preconditions.checkArgumentNonnegative(beforeLength); + Preconditions.checkArgumentNonnegative(afterLength); return mTarget.getSurroundingText(beforeLength, afterLength, flags); } diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index ab6dcb1b27a7..7db35d4bf8b5 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -318,7 +318,8 @@ public final class TextClassification implements Parcelable { public static PendingIntent createPendingIntent( @NonNull final Context context, @NonNull final Intent intent, int requestCode) { return PendingIntent.getActivity( - context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); + context, requestCode, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } /** diff --git a/packages/Tethering/src/android/net/util/TetheringMessageBase.java b/core/java/android/window/DisplayAreaAppearedInfo.aidl index 29c0a817b6f4..365f3e56d968 100644 --- a/packages/Tethering/src/android/net/util/TetheringMessageBase.java +++ b/core/java/android/window/DisplayAreaAppearedInfo.aidl @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.net.util; + +package android.window; /** - * This class defines Message.what base addresses for various state machine. + * Data object for the DisplayArea info provided when a DisplayArea is presented to an organizer. + * + * @hide */ -public class TetheringMessageBase { - public static final int BASE_MAIN_SM = 0; - public static final int BASE_IPSERVER = 100; - -} +parcelable DisplayAreaAppearedInfo; diff --git a/core/java/android/window/DisplayAreaAppearedInfo.java b/core/java/android/window/DisplayAreaAppearedInfo.java new file mode 100644 index 000000000000..d33d77d54031 --- /dev/null +++ b/core/java/android/window/DisplayAreaAppearedInfo.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.SurfaceControl; + +/** + * Data object for the DisplayArea info provided when a DisplayArea is presented to an organizer. + * + * @hide + */ +@TestApi +public final class DisplayAreaAppearedInfo implements Parcelable { + + @NonNull + private final DisplayAreaInfo mDisplayAreaInfo; + + @NonNull + private final SurfaceControl mLeash; + + @NonNull + public static final Creator<DisplayAreaAppearedInfo> CREATOR = + new Creator<DisplayAreaAppearedInfo>() { + @Override + public DisplayAreaAppearedInfo createFromParcel(Parcel source) { + final DisplayAreaInfo displayAreaInfo = source.readTypedObject(DisplayAreaInfo.CREATOR); + final SurfaceControl leash = source.readTypedObject(SurfaceControl.CREATOR); + return new DisplayAreaAppearedInfo(displayAreaInfo, leash); + } + + @Override + public DisplayAreaAppearedInfo[] newArray(int size) { + return new DisplayAreaAppearedInfo[size]; + } + + }; + + public DisplayAreaAppearedInfo(@NonNull DisplayAreaInfo displayAreaInfo, + @NonNull SurfaceControl leash) { + mDisplayAreaInfo = displayAreaInfo; + mLeash = leash; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mDisplayAreaInfo, flags); + dest.writeTypedObject(mLeash, flags); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * @return the DisplayArea info. + */ + @NonNull + public DisplayAreaInfo getDisplayAreaInfo() { + return mDisplayAreaInfo; + } + + /** + * @return the leash for the DisplayArea. + */ + @NonNull + public SurfaceControl getLeash() { + return mLeash; + } +} diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 38b2190a57f3..6ec093e045fa 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -16,12 +16,15 @@ package android.window; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.os.RemoteException; import android.view.SurfaceControl; +import java.util.List; + /** * Interface for WindowManager to delegate control of display areas. * @hide @@ -84,10 +87,17 @@ public class DisplayAreaOrganizer extends WindowOrganizer { */ public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1; + /** + * Registers a DisplayAreaOrganizer to manage display areas for a given feature. + * + * @return a list of display areas that should be managed by the organizer. + */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - public void registerOrganizer(int displayAreaFeature) { + @CallSuper + @NonNull + public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) { try { - getController().registerOrganizer(mInterface, displayAreaFeature); + return getController().registerOrganizer(mInterface, displayAreaFeature).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -97,6 +107,7 @@ public class DisplayAreaOrganizer extends WindowOrganizer { * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @CallSuper public void unregisterOrganizer() { try { getController().unregisterOrganizer(mInterface); @@ -105,6 +116,11 @@ public class DisplayAreaOrganizer extends WindowOrganizer { } } + /** + * Called when a DisplayArea of the registered window type can be controlled by this organizer. + * It will not be called for the DisplayAreas that exist when {@link #registerOrganizer(int)} is + * called. + */ public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo, @NonNull SurfaceControl leash) {} diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl index 41b9d027344e..8943847073c7 100644 --- a/core/java/android/window/IDisplayAreaOrganizerController.aidl +++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl @@ -16,13 +16,20 @@ package android.window; +import android.content.pm.ParceledListSlice; +import android.window.DisplayAreaAppearedInfo; import android.window.IDisplayAreaOrganizer; /** @hide */ interface IDisplayAreaOrganizerController { - /** Register a DisplayAreaOrganizer to manage display areas for a given feature. */ - void registerOrganizer(in IDisplayAreaOrganizer organizer, int displayAreaFeature); + /** + * Registers a DisplayAreaOrganizer to manage display areas for a given feature. + * + * @return a list of display areas that should be managed by the organizer. + */ + ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer(in IDisplayAreaOrganizer organizer, + int displayAreaFeature); /** * Unregisters a previously registered display area organizer. diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 52dc7e646d29..762297d15e6d 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -193,7 +193,7 @@ public class SuspendedAppActivity extends AlertActivity try { mSuspendingAppResources = createContextAsUser( UserHandle.of(mUserId), /* flags */ 0).getPackageManager() - .getResourcesForApplication(mSuspendedPackage); + .getResourcesForApplication(mSuspendingPackage); } catch (PackageManager.NameNotFoundException ne) { Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne); } diff --git a/core/java/com/android/internal/app/TEST_MAPPING b/core/java/com/android/internal/app/TEST_MAPPING index 373a5d9413a5..8bd791297e9d 100644 --- a/core/java/com/android/internal/app/TEST_MAPPING +++ b/core/java/com/android/internal/app/TEST_MAPPING @@ -1,4 +1,10 @@ { + "presubmit": [ + { + "name": "CtsSuspendAppsTestCases", + "file_patterns": ["(/|^)SuspendedAppActivity\\.java"] + } + ], "postsubmit": [ { "name": "FrameworksCoreTests", @@ -17,4 +23,4 @@ ] } ] -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java index 4eb7dede7769..43984b59378c 100644 --- a/core/java/com/android/internal/net/LegacyVpnInfo.java +++ b/core/java/com/android/internal/net/LegacyVpnInfo.java @@ -83,8 +83,8 @@ public class LegacyVpnInfo implements Parcelable { * Return best matching {@link LegacyVpnInfo} state based on given * {@link NetworkInfo}. */ - public static int stateFromNetworkInfo(NetworkInfo info) { - switch (info.getDetailedState()) { + public static int stateFromNetworkInfo(NetworkInfo.DetailedState state) { + switch (state) { case CONNECTING: return STATE_CONNECTING; case CONNECTED: @@ -94,8 +94,7 @@ public class LegacyVpnInfo implements Parcelable { case FAILED: return STATE_FAILED; default: - Log.w(TAG, "Unhandled state " + info.getDetailedState() - + " ; treating as disconnected"); + Log.w(TAG, "Unhandled state " + state + " ; treating as disconnected"); return STATE_DISCONNECTED; } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index e848da9b8ee3..f89e52d4f91d 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2540,6 +2540,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + params.backgroundBlurRadius = a.getDimensionPixelSize( + R.styleable.Window_windowBackgroundBlurRadius, 0); + if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( R.styleable.Window_windowAnimationStyle, 0); diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java index ab58d351d3b9..01cc1ed37ad4 100644 --- a/core/java/com/android/internal/protolog/common/ProtoLog.java +++ b/core/java/com/android/internal/protolog/common/ProtoLog.java @@ -35,6 +35,10 @@ package com.android.internal.protolog.common; * during build. */ public class ProtoLog { + + // Needs to be set directly otherwise the protologtool tries to transform the method call + public static boolean REQUIRE_PROTOLOGTOOL = true; + /** * DEBUG level log. * @@ -44,8 +48,10 @@ public class ProtoLog { */ public static void d(IProtoLogGroup group, String messageString, Object... args) { // Stub, replaced by the ProtoLogTool. - throw new UnsupportedOperationException( - "ProtoLog calls MUST be processed with ProtoLogTool"); + if (REQUIRE_PROTOLOGTOOL) { + throw new UnsupportedOperationException( + "ProtoLog calls MUST be processed with ProtoLogTool"); + } } /** @@ -57,8 +63,10 @@ public class ProtoLog { */ public static void v(IProtoLogGroup group, String messageString, Object... args) { // Stub, replaced by the ProtoLogTool. - throw new UnsupportedOperationException( - "ProtoLog calls MUST be processed with ProtoLogTool"); + if (REQUIRE_PROTOLOGTOOL) { + throw new UnsupportedOperationException( + "ProtoLog calls MUST be processed with ProtoLogTool"); + } } /** @@ -70,8 +78,10 @@ public class ProtoLog { */ public static void i(IProtoLogGroup group, String messageString, Object... args) { // Stub, replaced by the ProtoLogTool. - throw new UnsupportedOperationException( - "ProtoLog calls MUST be processed with ProtoLogTool"); + if (REQUIRE_PROTOLOGTOOL) { + throw new UnsupportedOperationException( + "ProtoLog calls MUST be processed with ProtoLogTool"); + } } /** @@ -83,8 +93,10 @@ public class ProtoLog { */ public static void w(IProtoLogGroup group, String messageString, Object... args) { // Stub, replaced by the ProtoLogTool. - throw new UnsupportedOperationException( - "ProtoLog calls MUST be processed with ProtoLogTool"); + if (REQUIRE_PROTOLOGTOOL) { + throw new UnsupportedOperationException( + "ProtoLog calls MUST be processed with ProtoLogTool"); + } } /** @@ -96,8 +108,10 @@ public class ProtoLog { */ public static void e(IProtoLogGroup group, String messageString, Object... args) { // Stub, replaced by the ProtoLogTool. - throw new UnsupportedOperationException( - "ProtoLog calls MUST be processed with ProtoLogTool"); + if (REQUIRE_PROTOLOGTOOL) { + throw new UnsupportedOperationException( + "ProtoLog calls MUST be processed with ProtoLogTool"); + } } /** @@ -109,7 +123,9 @@ public class ProtoLog { */ public static void wtf(IProtoLogGroup group, String messageString, Object... args) { // Stub, replaced by the ProtoLogTool. - throw new UnsupportedOperationException( - "ProtoLog calls MUST be processed with ProtoLogTool"); + if (REQUIRE_PROTOLOGTOOL) { + throw new UnsupportedOperationException( + "ProtoLog calls MUST be processed with ProtoLogTool"); + } } } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index f086dd79758b..e05aa8351681 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -17,6 +17,7 @@ package com.android.internal.view; import android.annotation.AnyThread; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.inputmethodservice.AbstractInputMethodService; @@ -106,9 +107,13 @@ public class InputConnectionWrapper implements InputConnection { return null; } + /** + * See {@link InputConnection#getTextAfterCursor(int, int)}. + */ + @Nullable @AnyThread - public CharSequence getTextAfterCursor(int length, int flags) { - if (mCancellationGroup.isCanceled()) { + public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0 || mCancellationGroup.isCanceled()) { return null; } @@ -122,9 +127,13 @@ public class InputConnectionWrapper implements InputConnection { return getResultOrNull(value, "getTextAfterCursor()"); } + /** + * See {@link InputConnection#getTextBeforeCursor(int, int)}. + */ + @Nullable @AnyThread - public CharSequence getTextBeforeCursor(int length, int flags) { - if (mCancellationGroup.isCanceled()) { + public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0 || mCancellationGroup.isCanceled()) { return null; } @@ -171,10 +180,12 @@ public class InputConnectionWrapper implements InputConnection { * not support this protocol. */ @AnyThread - public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) { - if (mCancellationGroup.isCanceled()) { + public SurroundingText getSurroundingText( + @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) { + if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) { return null; } + if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) { // This method is not implemented. return null; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 4f97975838d5..661bbed8abcb 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -1,4 +1,3 @@ - cc_library_shared { name: "libandroid_runtime", host_supported: true, @@ -286,8 +285,9 @@ cc_library_shared { ], include_dirs: [ "external/vulkan-headers/include", + "frameworks/native/libs/math/include", "frameworks/native/libs/nativebase/include", - "frameworks/native/libs/nativewindow/include" + "frameworks/native/libs/nativewindow/include", ], shared_libs: [ "libicui18n", @@ -303,10 +303,36 @@ cc_library_shared { linux_glibc: { srcs: [ "android_content_res_ApkAssets.cpp", + "android_hardware_input_InputApplicationHandle.cpp", "android_os_MessageQueue.cpp", + "android_os_Parcel.cpp", + + "android_view_KeyCharacterMap.cpp", + "android_view_KeyEvent.cpp", + "android_view_InputChannel.cpp", + "android_view_InputDevice.cpp", + "android_view_InputEventReceiver.cpp", + "android_view_InputEventSender.cpp", + "android_view_MotionEvent.cpp", + "android_view_VelocityTracker.cpp", + "android_view_VerifiedKeyEvent.cpp", + "android_view_VerifiedMotionEvent.cpp", + "android_util_AssetManager.cpp", + "android_util_Binder.cpp", + "android_util_FileObserver.cpp", ], + static_libs: [ + "libinput", + "libbinderthreadstateutils", + ], + shared_libs: [ + // libbinder needs to be shared since it has global state + // (e.g. gDefaultServiceManager) + "libbinder", + "libhidlbase", // libhwbinder is in here + ], }, windows: { enabled: true, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 32b8fa61c8a0..a3cb4c076593 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -40,6 +40,7 @@ #include <binder/Stability.h> #include <binderthreadstate/CallerUtils.h> #include <cutils/atomic.h> +#include <cutils/threads.h> #include <log/log.h> #include <utils/KeyedVector.h> #include <utils/List.h> diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 62f844eb59a8..94151b522c9c 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -110,6 +110,7 @@ static struct { jfieldID sourceCrop; jfieldID frameScale; jfieldID captureSecureLayers; + jfieldID allowProtected; } gCaptureArgsClassInfo; static struct { @@ -196,6 +197,7 @@ static struct { jclass clazz; jmethodID ctor; jfieldID defaultConfig; + jfieldID allowGroupSwitching; jfieldID primaryRefreshRateMin; jfieldID primaryRefreshRateMax; jfieldID appRequestRefreshRateMin; @@ -367,6 +369,8 @@ static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScale); captureArgs.captureSecureLayers = env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers); + captureArgs.allowProtected = + env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected); } static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, @@ -1003,6 +1007,9 @@ static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jo jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs, gDesiredDisplayConfigSpecsClassInfo.defaultConfig); + jboolean allowGroupSwitching = + env->GetBooleanField(desiredDisplayConfigSpecs, + gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching); jfloat primaryRefreshRateMin = env->GetFloatField(desiredDisplayConfigSpecs, gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin); @@ -1017,6 +1024,7 @@ static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jo gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax); size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig, + allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin, @@ -1029,11 +1037,13 @@ static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, job if (token == nullptr) return nullptr; int32_t defaultConfig; + bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; float appRequestRefreshRateMin; float appRequestRefreshRateMax; if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig, + &allowGroupSwitching, &primaryRefreshRateMin, &primaryRefreshRateMax, &appRequestRefreshRateMin, @@ -1044,8 +1054,8 @@ static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, job return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz, gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig, - primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin, - appRequestRefreshRateMax); + allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax, + appRequestRefreshRateMin, appRequestRefreshRateMax); } static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { @@ -1862,9 +1872,11 @@ int register_android_view_SurfaceControl(JNIEnv* env) gDesiredDisplayConfigSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz); gDesiredDisplayConfigSpecsClassInfo.ctor = - GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IFFFF)V"); + GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V"); gDesiredDisplayConfigSpecsClassInfo.defaultConfig = GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I"); + gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching = + GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z"); gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin = GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F"); gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax = @@ -1881,6 +1893,8 @@ int register_android_view_SurfaceControl(JNIEnv* env) gCaptureArgsClassInfo.frameScale = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScale", "F"); gCaptureArgsClassInfo.captureSecureLayers = GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z"); + gCaptureArgsClassInfo.allowProtected = + GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z"); jclass displayCaptureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayCaptureArgs"); diff --git a/core/proto/android/providers/settings/config.proto b/core/proto/android/providers/settings/config.proto index f343c3a30927..c5ea2ab0b868 100644 --- a/core/proto/android/providers/settings/config.proto +++ b/core/proto/android/providers/settings/config.proto @@ -31,6 +31,7 @@ message ConfigSettingsProto { repeated SettingProto activity_manager_settings = 4; repeated SettingProto alarm_manager_settings = 26; repeated SettingProto app_compat_settings = 5; + repeated SettingProto app_standby_settings = 27; repeated SettingProto autofill_settings = 6; repeated SettingProto blobstore_settings = 23; repeated SettingProto connectivity_settings = 7; @@ -52,7 +53,7 @@ message ConfigSettingsProto { repeated SettingProto telephony_settings = 21; repeated SettingProto textclassifier_settings = 22; - // Next tag: 27 + // Next tag: 28 message NamespaceProto { optional string namespace = 1; diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 9291a90574cd..1ea1bafd4d2f 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -78,7 +78,7 @@ message GlobalSettingsProto { option (android.msg_privacy).dest = DEST_EXPLICIT; // These are key=value lists, separated by commas. - optional SettingProto idle_constants = 1; + reserved 1; // idle_constants optional SettingProto standby_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto auto_restriction_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto forced_app_standby_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 221bcf6a800c..34c7fa734f21 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -39,10 +39,6 @@ android:layout_height="@dimen/notification_progress_bar_height" android:layout_marginTop="@dimen/notification_progress_margin_top" layout="@layout/notification_template_progress" /> - <include layout="@layout/notification_template_smart_reply_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/notification_content_margin" /> </LinearLayout> <include layout="@layout/notification_template_right_icon" /> </FrameLayout> diff --git a/core/res/res/layout/notification_template_top_line.xml b/core/res/res/layout/notification_template_top_line.xml index 27fab859a045..0786e138559f 100644 --- a/core/res/res/layout/notification_template_top_line.xml +++ b/core/res/res/layout/notification_template_top_line.xml @@ -102,9 +102,8 @@ android:id="@+id/alerted_icon" android:layout_width="@dimen/notification_alerted_size" android:layout_height="@dimen/notification_alerted_size" - android:layout_gravity="center" android:layout_marginStart="4dp" - android:paddingTop="1dp" + android:baseline="10dp" android:scaleType="fitCenter" android:visibility="gone" android:contentDescription="@string/notification_alerted_content_description" @@ -116,8 +115,7 @@ android:layout_height="@dimen/notification_feedback_size" android:layout_marginStart="6dp" android:layout_marginEnd="6dp" - android:paddingTop="2dp" - android:layout_gravity="center" + android:baseline="10dp" android:scaleType="fitCenter" android:src="@drawable/ic_feedback_indicator" android:background="?android:selectableItemBackgroundBorderless" @@ -128,9 +126,8 @@ android:id="@+id/profile_badge" android:layout_width="@dimen/notification_badge_size" android:layout_height="@dimen/notification_badge_size" - android:layout_gravity="center" android:layout_marginStart="4dp" - android:paddingTop="1dp" + android:baseline="10dp" android:scaleType="fitCenter" android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index b74f96de33b1..03e64a4038ad 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2181,6 +2181,10 @@ the decor view. --> <attr name="windowLightNavigationBar" format="boolean" /> + <!-- @hide --> + <attr name="windowBackgroundBlurRadius" format="dimension"/> + + <!-- Controls how the window is laid out if there is a {@code DisplayCutout}. <p> Defaults to {@code default}. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 68fd7c71bcfd..df03ccc784d4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3358,6 +3358,10 @@ ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. --> <item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item> + <!-- The maximum number of actions that is supported for picture-in-picture. This number + must be no less than 3 for back compatibility. --> + <integer name="config_pictureInPictureMaxNumberOfActions">3</integer> + <!-- Controls the snap mode for the docked stack divider 0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio 1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio) diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 39989dd24a23..1e9a747d30dc 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3047,6 +3047,8 @@ <public name="rollbackDataPolicy" /> <public name="allowClickWhenDisabled" /> <public name="windowLayoutAffinity" /> + <!-- @hide --> + <public name="windowBackgroundBlurRadius"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8daa6539ff64..a716875b068f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -409,6 +409,7 @@ <java-symbol type="integer" name="config_defaultPictureInPictureGravity" /> <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" /> <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> + <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" /> <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" /> <java-symbol type="integer" name="config_bluetooth_max_advertisers" /> <java-symbol type="integer" name="config_bluetooth_max_scan_filters" /> diff --git a/core/tests/BTtraffic/Android.bp b/core/tests/BTtraffic/Android.bp deleted file mode 100644 index e508570daf03..000000000000 --- a/core/tests/BTtraffic/Android.bp +++ /dev/null @@ -1,7 +0,0 @@ -android_app { - name: "bttraffic", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - sdk_version: "current", - certificate: "platform", -} diff --git a/core/tests/BTtraffic/AndroidManifest.xml b/core/tests/BTtraffic/AndroidManifest.xml deleted file mode 100644 index 00d9707de2bf..000000000000 --- a/core/tests/BTtraffic/AndroidManifest.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.google.android.experimental.bttraffic" > - - <uses-permission android:name="android.permission.BLUETOOTH"/> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> - - <uses-sdk - android:minSdkVersion="18" - android:targetSdkVersion="18" - /> - <application - android:allowBackup="false" - android:label="@string/app_name" > - <service - android:name=".BTtraffic" - android:enabled="true" - android:exported="true" > - </service> - </application> - -</manifest> diff --git a/core/tests/BTtraffic/README b/core/tests/BTtraffic/README deleted file mode 100644 index 430488f656f9..000000000000 --- a/core/tests/BTtraffic/README +++ /dev/null @@ -1,45 +0,0 @@ -This is a tool to generate classic Bluetooth traffic with specified period and package size. -Together with the SvcMonitor, which will be called automatically in this android service, can be -used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service -com.android.bluetooth. - -1. Server (Listener) - Client (Sender) model. Both run as an Android service. -2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by -providing the MAC addr of the server. -3. Bluetooth has to be turned on on both side. -4. Client can configure the traffic by specifying the transfer period and package size. -5. A separate monitor process will be automatically forked and will be reading from /proc file -system to calculate the cpu usage. The measurement is updated once per second. -6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an -independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer -to SvcMonitor's README for usage and details. - -Usage: -To instal the test: -On both the server and client device, install the 2 apk: -$ adb install $OUT/system/app/bttraffic/bttraffic.apk -$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk - -To start the service on the SERVER side: -$ adb shell am startservice -a start --ez ack true \ -com.google.android.experimental.bttraffic/.BTtraffic - -To start the service on the CLIENT side: -$ adb shell am startservice -a start \ --e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \ -com.google.android.experimental.bttraffic/.BTtraffic - -To stop the test: -On either the server or client: -$ adb shell am startservice -a stop \ -com.google.android.experimental.bttraffic/.BTtraffic - -To look at the data: -$ adb logcat | grep bttraffic - -Options: --e addr: MAC addr of the server, in uppercase letter. ---ei size: package size, unit: byte; default: 1024, MAX: 20MB ---ei period: system sleep time between sending each package, unit: ms, default: 5000 - ** if -1 is provided, client will only send the package once. ---ez ack: whether acknowledge is required (true/false) diff --git a/core/tests/BTtraffic/res/values/strings.xml b/core/tests/BTtraffic/res/values/strings.xml deleted file mode 100644 index e70276e03647..000000000000 --- a/core/tests/BTtraffic/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ -<resources> - <string name="app_name">Bluetooth Test</string> -</resources> diff --git a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java deleted file mode 100644 index 286c0aa2915f..000000000000 --- a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java +++ /dev/null @@ -1,328 +0,0 @@ -package com.google.android.experimental.bttraffic; - -import android.app.Service; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothServerSocket; -import android.bluetooth.BluetoothSocket; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.os.SystemClock; -import android.util.Log; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.Exception; -import java.lang.Runtime; -import java.lang.RuntimeException; -import java.lang.Process; -import java.nio.ByteBuffer; -import java.util.Random; -import java.util.Set; -import java.util.UUID; - -public class BTtraffic extends Service { - public static final String TAG = "bttraffic"; - static final String SERVICE_NAME = "bttraffic"; - static final String SYS_SERVICE_NAME = "com.android.bluetooth"; - static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67"); - volatile Thread mWorkerThread; - volatile boolean isShuttingDown = false; - volatile boolean isServer = false; - - public BTtraffic() {} - - static void safeClose(Closeable closeable) { - try { - closeable.close(); - } catch (IOException e) { - Log.d(TAG, "Unable to close resource.\n"); - } - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (intent == null) { - stopSelf(); - return 0; - } - if ("stop".equals(intent.getAction())) { - stopService(); - } else if ("start".equals(intent.getAction())) { - startWorker(intent); - } else { - Log.d(TAG, "unknown action: + " + intent.getAction()); - } - return 0; - } - - private void startWorker(Intent intent) { - if (mWorkerThread != null) { - Log.d(TAG, "worker thread already active"); - return; - } - isShuttingDown = false; - String remoteAddr = intent.getStringExtra("addr"); - Log.d(TAG, "startWorker: addr=" + remoteAddr); - Runnable worker = - remoteAddr == null - ? new ListenerRunnable(this, intent) - : new SenderRunnable(this, remoteAddr, intent); - isServer = remoteAddr == null ? true: false; - mWorkerThread = new Thread(worker, "BTtrafficWorker"); - try { - startMonitor(); - Log.d(TAG, "Monitor service started"); - mWorkerThread.start(); - Log.d(TAG, "Worker thread started"); - } catch (Exception e) { - Log.d(TAG, "Failed to start service", e); - } - } - - private void startMonitor() - throws Exception { - if (isServer) { - Log.d(TAG, "Start monitor on server"); - String[] startmonitorCmd = { - "/system/bin/am", - "startservice", - "-a", "start", - "-e", "java", SERVICE_NAME, - "-e", "hal", SYS_SERVICE_NAME, - "com.google.android.experimental.svcmonitor/.SvcMonitor" - }; - Process ps = new ProcessBuilder() - .command(startmonitorCmd) - .redirectErrorStream(true) - .start(); - } else { - Log.d(TAG, "No need to start SvcMonitor on client"); - } - } - - private void stopMonitor() - throws Exception { - if (isServer) { - Log.d(TAG, "StopMonitor on server"); - String[] stopmonitorCmd = { - "/system/bin/am", - "startservice", - "-a", "stop", - "com.google.android.experimental.svcmonitor/.SvcMonitor" - }; - Process ps = new ProcessBuilder() - .command(stopmonitorCmd) - .redirectErrorStream(true) - .start(); - } else { - Log.d(TAG, "No need to stop Svcmonitor on client"); - } - } - - public void stopService() { - if (mWorkerThread == null) { - Log.d(TAG, "no active thread"); - return; - } - - isShuttingDown = true; - - try { - stopMonitor(); - } catch (Exception e) { - Log.d(TAG, "Unable to stop SvcMonitor!", e); - } - - if (Thread.currentThread() != mWorkerThread) { - mWorkerThread.interrupt(); - Log.d(TAG, "Interrupting thread"); - try { - mWorkerThread.join(); - } catch (InterruptedException e) { - Log.d(TAG, "Unable to join thread!"); - } - } - - mWorkerThread = null; - stopSelf(); - Log.d(TAG, "Service stopped"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - - @Override - public IBinder onBind(Intent intent) { - throw new UnsupportedOperationException("Not yet implemented"); - } - - public static class ListenerRunnable implements Runnable { - private final BTtraffic bttraffic; - private final boolean sendAck; - private Intent intent; - private final int maxbuffersize = 20 * 1024 * 1024; - - public ListenerRunnable(BTtraffic bttraffic, Intent intent) { - this.bttraffic = bttraffic; - this.sendAck = intent.getBooleanExtra("ack", true); - this.intent = intent; - } - - @Override - public void run() { - BluetoothServerSocket serverSocket; - - try { - Log.d(TAG, "getting server socket"); - serverSocket = BluetoothAdapter.getDefaultAdapter() - .listenUsingInsecureRfcommWithServiceRecord( - SERVICE_NAME, SERVICE_UUID); - } catch (IOException e) { - Log.d(TAG, "error creating server socket, stopping thread"); - bttraffic.stopService(); - return; - } - - Log.d(TAG, "got server socket, starting accept loop"); - BluetoothSocket socket = null; - try { - Log.d(TAG, "accepting"); - socket = serverSocket.accept(); - - if (!Thread.interrupted()) { - Log.d(TAG, "accepted, listening"); - doListening(socket.getInputStream(), socket.getOutputStream()); - Log.d(TAG, "listen finished"); - } - } catch (IOException e) { - Log.d(TAG, "error while accepting or listening", e); - } finally { - Log.d(TAG, "Linster interruped"); - Log.d(TAG, "closing socket and stopping service"); - safeClose(serverSocket); - safeClose(socket); - if (!bttraffic.isShuttingDown) - bttraffic.stopService(); - } - - } - - private void doListening(InputStream inputStream, OutputStream outputStream) - throws IOException { - ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize); - - while (!Thread.interrupted()) { - readBytesIntoBuffer(inputStream, byteBuffer, 4); - byteBuffer.flip(); - int length = byteBuffer.getInt(); - if (Thread.interrupted()) - break; - readBytesIntoBuffer(inputStream, byteBuffer, length); - - if (sendAck) - outputStream.write(0x55); - } - } - - void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead) - throws IOException { - byteBuffer.clear(); - while (true) { - int position = byteBuffer.position(); - int remaining = numToRead - position; - if (remaining == 0) { - break; - } - int count = inputStream.read(byteBuffer.array(), position, remaining); - if (count < 0) { - throw new IOException("read the EOF"); - } - byteBuffer.position(position + count); - } - } - } - - public static class SenderRunnable implements Runnable { - private final BTtraffic bttraffic; - private final String remoteAddr; - private final int pkgsize, period; - private final int defaultpkgsize = 1024; - private final int defaultperiod = 5000; - private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4); - - public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) { - this.bttraffic = bttraffic; - this.remoteAddr = remoteAddr; - this.pkgsize = intent.getIntExtra("size", defaultpkgsize); - this.period = intent.getIntExtra("period", defaultperiod); - } - - @Override - public void run() { - BluetoothDevice device = null; - try { - device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr); - } catch (IllegalArgumentException e) { - Log.d(TAG, "Invalid BT MAC address!\n"); - } - if (device == null) { - Log.d(TAG, "can't find matching device, stopping thread and service"); - bttraffic.stopService(); - return; - } - - BluetoothSocket socket = null; - try { - Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr); - socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID); - socket.connect(); - Log.d(TAG, "connected, starting to send"); - doSending(socket.getOutputStream()); - Log.d(TAG, "send stopped, stopping service"); - } catch (Exception e) { - Log.d(TAG, "error while sending", e); - } finally { - Log.d(TAG, "finishing, closing thread and service"); - safeClose(socket); - if (!bttraffic.isShuttingDown) - bttraffic.stopService(); - } - } - - private void doSending(OutputStream outputStream) throws IOException { - Log.w(TAG, "doSending"); - try { - Random random = new Random(System.currentTimeMillis()); - - byte[] bytes = new byte[pkgsize]; - random.nextBytes(bytes); - while (!Thread.interrupted()) { - writeBytes(outputStream, bytes.length); - outputStream.write(bytes, 0, bytes.length); - if (period < 0) - break; - if (period == 0) - continue; - - SystemClock.sleep(period); - } - Log.d(TAG, "Sender interrupted"); - } catch (IOException e) { - Log.d(TAG, "doSending got error", e); - } - } - - private static void writeBytes(OutputStream outputStream, int value) throws IOException { - lengthBuffer.putInt(value); - lengthBuffer.flip(); - outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit()); - } - } - -} diff --git a/core/tests/SvcMonitor/Android.bp b/core/tests/SvcMonitor/Android.bp deleted file mode 100644 index 606e87cb0f4d..000000000000 --- a/core/tests/SvcMonitor/Android.bp +++ /dev/null @@ -1,7 +0,0 @@ -android_app { - name: "svcmonitor", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - sdk_version: "current", - certificate: "platform", -} diff --git a/core/tests/SvcMonitor/AndroidManifest.xml b/core/tests/SvcMonitor/AndroidManifest.xml deleted file mode 100644 index de5a9bdaed41..000000000000 --- a/core/tests/SvcMonitor/AndroidManifest.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.google.android.experimental.svcmonitor" > - - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> - - <uses-sdk - android:minSdkVersion="18" - android:targetSdkVersion="18" - /> - <application - android:allowBackup="false" - android:label="@string/app_name" > - <service - android:name=".SvcMonitor" - android:enabled="true" - android:exported="true" > - </service> - </application> - -</manifest> diff --git a/core/tests/SvcMonitor/README b/core/tests/SvcMonitor/README deleted file mode 100644 index 13a4380589b4..000000000000 --- a/core/tests/SvcMonitor/README +++ /dev/null @@ -1,27 +0,0 @@ -This Android service measures CPU usage of a program and an underlying system service it relies on. -An example of this would be an android app XYZ communicates to some other device via Bluetooth. The -SvcMonitor service can monitor the CPU usage of XYZ and com.android.bluetooth. - -Usage: - -To start the service: -$ adb shell am startservice -a start \ --e java XYZ -e hal com.android.bluetooth \ -com.google.android.experimental.svcmonitor/.SvcMonitor - -To stop the service: -$ adb shell am startservice -a stop \ -com.google.android.experimental.svcmonitor/.SvcMonitor - -To stop the service config: -$ adb shell am startservice -a change \ --e java NewName -e hal NewService \ -com.google.android.experimental.svcmonitor/.SvcMonitor - -To monitor the data: -$ adb logcat | grep XYZ - -Options: --e java NameOfProgram: any running process’s name. --e hal NameOfSysService: name of the system service the previous process relies on. ---ei period: period between each measurement (frequency). Unit: ms, Default:1000, Min: 100 diff --git a/core/tests/SvcMonitor/res/values/strings.xml b/core/tests/SvcMonitor/res/values/strings.xml deleted file mode 100644 index e70276e03647..000000000000 --- a/core/tests/SvcMonitor/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ -<resources> - <string name="app_name">Bluetooth Test</string> -</resources> diff --git a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java deleted file mode 100644 index a451445530cd..000000000000 --- a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.google.android.experimental.svcmonitor; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; -import android.os.SystemClock; -import android.util.Log; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.lang.Runnable; -import java.lang.Thread; -import java.util.Set; - -public class SvcMonitor extends Service { - public static final String TAG = "svcmonitor"; - String javaProc, halProc; - volatile Thread tMonitor; - int period; - - public SvcMonitor() {}; - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (intent == null) { - stopSelf(); - return 0; - } - Log.d(TAG, "Starting SvcMonitor"); - if ("stop".equals(intent.getAction())) { - stopService(); - } else if ("start".equals(intent.getAction())) { - startMonitor(intent); - } else if ("change".equals(intent.getAction())) { - changeConfig(intent); - } else { - Log.d(TAG, "unknown action: + " + intent.getAction()); - } - return 0; - } - - private void changeConfig(Intent intent) { - if (tMonitor == null) { - Log.d(TAG, "Service not active. Start service first"); - return; - } - stopThread(); - startMonitor(intent); - } - - private void startMonitor(Intent intent) { - if (tMonitor != null) { - Log.d(TAG, "thread already active"); - return; - } - javaProc = intent.getStringExtra("java"); - halProc = intent.getStringExtra("hal"); - period = intent.getIntExtra("period", 1000); - if (javaProc == null || halProc == null || period < 100) { - Log.d(TAG, "Failed starting monitor, invalid arguments."); - stopSelf(); - return; - } - Runnable monitor = new MonitorRunnable(this); - tMonitor = new Thread(monitor); - tMonitor.start(); - } - - private void stopService() { - stopThread(); - stopSelf(); - Log.d(TAG, "SvcMonitor stopped"); - } - - private void stopThread() { - if (tMonitor == null) { - Log.d(TAG, "no active thread"); - return; - } - Log.d(TAG, "interrupting monitor thread"); - tMonitor.interrupt(); - try { - tMonitor.join(); - } catch (InterruptedException e) { - Log.d(TAG, "Unable to finish monitor thread"); - } - tMonitor = null; - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - - @Override - public IBinder onBind(Intent intent) { - throw new UnsupportedOperationException("Not yet implemented"); - } - - public static class MonitorRunnable implements Runnable { - long java_time_old, hal_time_old, cpu_time_old = -1; - String javaPID, halPID; - SvcMonitor svcmonitor; - static String javaProcTAG; - int period; - - public MonitorRunnable(SvcMonitor svcmonitor) { - this.svcmonitor = svcmonitor; - this.period = svcmonitor.period; - javaPID = getPIDof(svcmonitor.javaProc); - halPID = getPIDof(svcmonitor.halProc); - java_time_old = getPsTime(javaPID); - hal_time_old = getPsTime(halPID); - cpu_time_old = getPsTime(""); - javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray()); - } - - @Override - public void run() { - if (halPID.isEmpty() || javaPID.isEmpty()) { - Log.d(javaProcTAG, "No such process: " + - (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc)); - return; - } - while (!Thread.interrupted()) { - calculateUsage(); - SystemClock.sleep(period); - } - Log.d(TAG, "Stopping monitor thread"); - } - - private void calculateUsage() { - long java_time = getPsTime(javaPID); - long hal_time = getPsTime(halPID); - long cpu_time = getPsTime(""); - - if (cpu_time_old >= 0) { - float java_diff = (float) (java_time - java_time_old); - float hal_diff = (float) (hal_time - hal_time_old); - float cpu_diff = (float) (cpu_time - cpu_time_old); - Log.w(javaProcTAG, "\n----------------\n"); - Log.w(javaProcTAG, "JAVA level CPU: " - + (java_diff * 100.0 / cpu_diff) + "%\n"); - Log.w(javaProcTAG, " HAL level CPU: " - + (hal_diff * 100.0 / cpu_diff) + "%\n"); - Log.w(javaProcTAG, " SYS level CPU: " - + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n"); - } else { - Log.w(TAG, "Waiting for status\n"); - } - - java_time_old = java_time; - hal_time_old = hal_time; - cpu_time_old = cpu_time; - } - - private String getPIDof(String psName) { - String pid = ""; - - try { - String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName}; - Process ps = Runtime.getRuntime().exec(cmd); - BufferedReader in = new BufferedReader( - new InputStreamReader(ps.getInputStream())); - String temp = in.readLine(); - if (temp == null || temp.isEmpty()) - throw new IOException("No such process: " + psName); - pid = temp.split(" +")[1]; - in.close(); - } catch (IOException e) { - Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e); - } - return pid; - } - - private long getPsTime(String pid) { - String psStat = getPsStat("/" + pid); - String[] statBreakDown = psStat.split(" +"); - long psTime; - - if (pid.isEmpty()) { - psTime = Long.parseLong(statBreakDown[1]) - + Long.parseLong(statBreakDown[2]) - + Long.parseLong(statBreakDown[3]) - + Long.parseLong(statBreakDown[4]); - } else { - psTime = Long.parseLong(statBreakDown[13]) - + Long.parseLong(statBreakDown[14]); - } - - return psTime; - } - - private String getPsStat(String psname) { - String stat = ""; - try { - FileInputStream fs = new FileInputStream("/proc" + psname + "/stat"); - BufferedReader br = new BufferedReader(new InputStreamReader(fs)); - stat = br.readLine(); - fs.close(); - } catch (IOException e) { - Log.d(TAG, "Error retreiving stat. \n"); - } - return stat; - } - } -} diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index f48e66681cc7..b517428f5b59 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -261,4 +261,12 @@ public class ContentResolverTest { // Expected } } + + @Test + public void testUncanonicalize() { + Uri uncanonical = mResolver.uncanonicalize( + Uri.parse("content://android.content.FakeProviderRemote/something")); + assertThat(uncanonical).isEqualTo( + Uri.parse("content://android.content.FakeProviderRemote/uncanonical")); + } } diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 2c92da34d9a4..d0bb78101ee4 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -66,4 +66,13 @@ public class FakeProviderRemote extends ContentProvider { return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority()) .appendPath("canonical").build(); } + + @Override + public Uri uncanonicalize(Uri uri) { + if (uri.getPath() != null && uri.getPath().contains("error")) { + throw new IllegalArgumentException("Expected exception"); + } + return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority()) + .appendPath("uncanonical").build(); + } } diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 54e8a0c1638e..bda84dd76cf5 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -528,7 +528,7 @@ </font> <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> - </family> + </family> <family lang="und-Khmr" variant="compact"> <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font> <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> @@ -762,18 +762,8 @@ <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font> </family> <family lang="und-Tibt"> - <font weight="400" style="normal">NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="700" /> - </font> + <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font> + <font weight="700" style="normal">NotoSansTibetan-Bold.ttf</font> </family> <family lang="und-Tfng"> <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> @@ -903,83 +893,4 @@ <family lang="und-Wara"> <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font> </family> - <family lang="und-Gran"> - <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font> - </family> - <family lang="und-Modi"> - <font weight="400" style="normal">NotoSansModi-Regular.ttf</font> - </family> - <family lang="und-Dogr"> - <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> - </family> - <family lang="und-Medf"> - <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="700" /> - </font> - </family> - <family lang="und-Soyo"> - <font weight="400" style="normal">NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="700" /> - </font> - </family> - <family lang="und-Takr"> - <font weight="400" style="normal">NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="700" /> - </font> - </family> - <family lang="und-Hmnp"> - <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="700" /> - </font> - </family> - <family lang="und-Yezi"> - <font weight="400" style="normal">NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="700" /> - </font> - </family> </familyset> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml index d2f235e273d5..9157f63ce1b3 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml @@ -16,7 +16,6 @@ --> <!-- Layout for {@link com.android.wm.shell.pip.tv.PipControlsView}. --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> - <com.android.wm.shell.pip.tv.PipControlButtonView android:id="@+id/full_button" android:layout_width="@dimen/picture_in_picture_button_width" @@ -31,13 +30,4 @@ android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" android:src="@drawable/pip_ic_close_white" android:text="@string/pip_close" /> - - <com.android.wm.shell.pip.tv.PipControlButtonView - android:id="@+id/play_pause_button" - android:layout_width="@dimen/picture_in_picture_button_width" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" - android:src="@drawable/pip_ic_pause_white" - android:text="@string/pip_pause" - android:visibility="gone" /> </merge> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index 17418f934691..bd6c1e096d01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -30,6 +30,7 @@ import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; +import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; import android.window.WindowContainerTransaction; @@ -189,6 +190,17 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { } @Override + public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) { + final List<DisplayAreaAppearedInfo> displayAreaInfos = + super.registerOrganizer(displayAreaFeature); + for (int i = 0; i < displayAreaInfos.size(); i++) { + final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); + onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); + } + return displayAreaInfos; + } + + @Override public void unregisterOrganizer() { super.unregisterOrganizer(); mUpdateHandler.post(() -> resetWindowsOffset(null)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 59c79bafd46c..8d5da1a5ffcb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -24,7 +24,6 @@ import android.content.pm.ActivityInfo; import android.graphics.Rect; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.pip.tv.PipController; import java.io.PrintWriter; import java.util.function.Consumer; @@ -34,12 +33,6 @@ import java.util.function.Consumer; */ public interface Pip { /** - * Registers a {@link PipController.MediaListener} to PipController. - */ - default void addMediaListener(PipController.MediaListener listener) { - } - - /** * Closes PIP (PIPed activity and PIP system UI). */ default void closePip() { @@ -145,12 +138,6 @@ public interface Pip { } /** - * Removes a {@link PipController.MediaListener} from PipController. - */ - default void removeMediaListener(PipController.MediaListener listener) { - } - - /** * Resize the Pip to the appropriate size for the input state. * * @param state In Pip state also used to determine the new size for the Pip. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index 3a675c49e712..d0ab31dd72c8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -42,7 +42,10 @@ public final class PipBoundsState { private final DisplayInfo mDisplayInfo = new DisplayInfo(); private final DisplayLayout mDisplayLayout = new DisplayLayout(); - void setBounds(@NonNull Rect bounds) { + /** + * Set the current PIP bounds. + */ + public void setBounds(@NonNull Rect bounds) { mBounds.set(bounds); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index d506ca8d6470..37a5919b1c9e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -29,7 +29,6 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.graphics.Rect; -import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -45,6 +44,7 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; @@ -62,7 +62,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private static final String TAG = "PipController"; private Context mContext; - private Handler mHandler = new Handler(); + private ShellExecutor mMainExecutor; private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); private final Rect mTmpInsetBounds = new Rect(); @@ -82,6 +82,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac protected PipMenuActivityController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; + protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = + new PipControllerPinnedStackListener(); /** * Handler for display rotation changes. @@ -150,12 +152,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PinnedStackListenerForwarder.PinnedStackListener { @Override public void onListenerRegistered(IPinnedStackController controller) { - mHandler.post(() -> mTouchHandler.setPinnedStackController(controller)); + mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller)); } @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight); mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); }); @@ -163,19 +165,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mHandler.post(() -> updateMovementBounds(null /* toBounds */, + mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */, false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */, null /* windowContainerTransaction */)); } @Override public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { - mHandler.post(() -> mMenuController.setAppActions(actions)); + mMainExecutor.execute(() -> mMenuController.setAppActions(actions)); } @Override public void onActivityHidden(ComponentName componentName) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { // The activity was removed, we don't want to restore to the reentry state // saved for this component anymore. @@ -186,12 +188,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> mPipBoundsState.setDisplayInfo(displayInfo)); + mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo)); } @Override public void onConfigurationChanged() { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsHandler.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); }); @@ -201,7 +203,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac public void onAspectRatioChanged(float aspectRatio) { // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params // change. - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsState.setAspectRatio(aspectRatio); mTouchHandler.onAspectRatioChanged(); }); @@ -217,7 +219,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper + WindowManagerShellWrapper windowManagerShellWrapper, + ShellExecutor mainExecutor ) { // Ensure that we are the primary user's SystemUI. final int processUser = UserManager.get(context).getUserHandle(); @@ -231,6 +234,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsHandler = pipBoundsHandler; mPipBoundsState = pipBoundsState; mPipTaskOrganizer = pipTaskOrganizer; + mMainExecutor = mainExecutor; mPipTaskOrganizer.registerPipTransitionCallback(this); mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> { final DisplayInfo newDisplayInfo = new DisplayInfo(); @@ -254,8 +258,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsState.setDisplayInfo(displayInfo); try { - mWindowManagerShellWrapper.addPinnedStackListener( - new PipControllerPinnedStackListener()); + mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); } catch (RemoteException e) { Slog.e(TAG, "Failed to register pinned stack listener", e); } @@ -263,14 +266,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onDensityOrFontScaleChanged() { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); }); } @Override public void onActivityPinned(String packageName) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); @@ -280,7 +283,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onActivityUnpinned(ComponentName topActivity) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mMenuController.onActivityUnpinned(); mTouchHandler.onActivityUnpinned(topActivity); mAppOpsListener.onActivityUnpinned(); @@ -299,7 +302,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onOverlayChanged() { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay())); updateMovementBounds(null /* toBounds */, false /* fromRotation */, false /* fromImeAdjustment */, @@ -358,7 +361,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac */ @Override public void setShelfHeight(boolean visible, int height) { - mHandler.post(() -> setShelfHeightLocked(visible, height)); + mMainExecutor.execute(() -> setShelfHeightLocked(visible, height)); } private void setShelfHeightLocked(boolean visible, int height) { @@ -374,12 +377,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void setPinnedStackAnimationType(int animationType) { - mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); + mMainExecutor.execute(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); } @Override public void setPinnedStackAnimationListener(Consumer<Boolean> callback) { - mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback); + mMainExecutor.execute(() -> mPinnedStackAnimationRecentsCallback = callback); } @Override @@ -476,7 +479,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsState pipBoundsState, PipMediaController pipMediaController, PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper) { + WindowManagerShellWrapper windowManagerShellWrapper, + ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { Slog.w(TAG, "Device doesn't support Pip feature"); return null; @@ -484,6 +488,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, pipMenuActivityController, - pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper); + pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, mainExecutor); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index b5fa03082401..9240b3f41ff4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -67,16 +67,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final Context mContext; private final PipTaskOrganizer mPipTaskOrganizer; - private final @NonNull PipBoundsState mPipBoundsState; + private @NonNull PipBoundsState mPipBoundsState; private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - /** PIP's current bounds on the screen. */ - private final Rect mBounds = new Rect(); - /** The bounds within which PIP's top-left coordinate is allowed to move. */ private final Rect mMovementBounds = new Rect(); @@ -140,7 +137,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> { mMainHandler.post(() -> { mMenuController.updateMenuLayout(newBounds); - mBounds.set(newBounds); + mPipBoundsState.setBounds(newBounds); }); }; @@ -196,7 +193,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mResizePipUpdateListener = (target, values) -> { if (!mTemporaryBounds.isEmpty()) { mPipTaskOrganizer.scheduleUserResizePip( - mBounds, mTemporaryBounds, null); + getBounds(), mTemporaryBounds, null); } }; } @@ -204,7 +201,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, @NonNull @Override public Rect getFloatingBoundsOnScreen() { - return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : mBounds; + return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : getBounds(); } @NonNull @@ -223,7 +220,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ void synchronizePinnedStackBounds() { cancelAnimations(); - mBounds.set(mPipBoundsState.getBounds()); mTemporaryBounds.setEmpty(); if (mPipTaskOrganizer.isInPip()) { @@ -261,10 +257,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, if (!isDragging) { resizePipUnchecked(toBounds); - mBounds.set(toBounds); + mPipBoundsState.setBounds(toBounds); } else { mTemporaryBounds.set(toBounds); - mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, + mPipTaskOrganizer.scheduleUserResizePip(getBounds(), mTemporaryBounds, (Rect newBounds) -> { mMainHandler.post(() -> { mMenuController.updateMenuLayout(newBounds); @@ -275,8 +271,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // If PIP is 'catching up' after being stuck in the dismiss target, update the animation // to spring towards the new touch location. mTemporaryBoundsPhysicsAnimator - .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig) - .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig) + .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig) + .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig) .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig) .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig); @@ -292,8 +288,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, boolean flung, Function0<Unit> after) { final PointF targetCenter = target.getCenterOnScreen(); - final float desiredWidth = mBounds.width() / 2; - final float desiredHeight = mBounds.height() / 2; + final float desiredWidth = getBounds().width() / 2; + final float desiredHeight = getBounds().height() / 2; final float destinationX = targetCenter.x - (desiredWidth / 2f); final float destinationY = targetCenter.y - (desiredHeight / 2f); @@ -301,7 +297,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // If we're already in the dismiss target area, then there won't be a move to set the // temporary bounds, so just initialize it to the current bounds if (mTemporaryBounds.isEmpty()) { - mTemporaryBounds.set(mBounds); + mTemporaryBounds.set(getBounds()); } mTemporaryBoundsPhysicsAnimator .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig) @@ -365,15 +361,17 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // The movement bounds represent the area within which we can move PIP's top-left position. // The allowed area for all of PIP is those bounds plus PIP's width and height. mFloatingAllowedArea.set(mMovementBounds); - mFloatingAllowedArea.right += mBounds.width(); - mFloatingAllowedArea.bottom += mBounds.height(); + mFloatingAllowedArea.right += getBounds().width(); + mFloatingAllowedArea.bottom += getBounds().height(); } /** * @return the PiP bounds. + * + * TODO(b/169373982): can be private, outside callers can use PipBoundsState directly. */ Rect getBounds() { - return mBounds; + return mPipBoundsState.getBounds(); } /** @@ -381,7 +379,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * otherwise. */ Rect getPossiblyAnimatingBounds() { - return mTemporaryBounds.isEmpty() ? mBounds : mTemporaryBounds; + return mTemporaryBounds.isEmpty() ? getBounds() : mTemporaryBounds; } /** @@ -407,8 +405,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mSpringingToTouch = false; mTemporaryBoundsPhysicsAnimator - .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig) - .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig) + .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig) + .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig) .flingThenSpring( FloatProperties.RECT_X, velocityX, isStash ? mStashConfigX : mFlingConfigX, mSpringConfig, true /* flingMustReachMinOrMax */) @@ -416,7 +414,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig) .withEndActions(endAction); - final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO); + final float offset = ((float) getBounds().width()) * (1.0f - STASH_RATIO); final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left; final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right; @@ -436,7 +434,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) { if (!mTemporaryBoundsPhysicsAnimator.isRunning()) { // Animate from the current bounds if we're not already animating. - mTemporaryBounds.set(mBounds); + mTemporaryBounds.set(getBounds()); } mTemporaryBoundsPhysicsAnimator @@ -453,13 +451,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // Animate off the bottom of the screen, then dismiss PIP. mTemporaryBoundsPhysicsAnimator .spring(FloatProperties.RECT_Y, - mMovementBounds.bottom + mBounds.height() * 2, + mMovementBounds.bottom + getBounds().height() * 2, 0, mSpringConfig) .withEndActions(this::dismissPip); startBoundsAnimator( - mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */, + getBounds().left /* toX */, getBounds().bottom + getBounds().height() /* toY */, true /* dismiss */); mDismissalPending = false; @@ -470,7 +468,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ float animateToExpandedState(Rect expandedBounds, Rect movementBounds, Rect expandedMovementBounds, Runnable callback) { - float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds); + float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()), + movementBounds); mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction); mPostPipTransitionCallback = callback; resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION); @@ -484,7 +483,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) { if (savedSnapFraction < 0f) { // If there are no saved snap fractions, then just use the current bounds - savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), + savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()), currentMovementBounds); } mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction); @@ -525,7 +524,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right); mFlingConfigY = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom); - final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO); + final float offset = ((float) getBounds().width()) * (1.0f - STASH_RATIO); mStashConfigX = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, mMovementBounds.left - offset, mMovementBounds.right + offset); } @@ -547,8 +546,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mAnimatingToBounds.set( (int) toX, (int) toY, - (int) toX + mBounds.width(), - (int) toY + mBounds.height()); + (int) toX + getBounds().width(), + (int) toY + getBounds().height()); setAnimatingToBounds(mAnimatingToBounds); if (!mTemporaryBoundsPhysicsAnimator.isRunning()) { @@ -572,11 +571,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) { - mBounds.set(mTemporaryBounds); + mPipBoundsState.setBounds(mTemporaryBounds); if (!mDismissalPending) { // do not schedule resize if PiP is dismissing, which may cause app re-open to // mBounds instead of it's normal bounds. - mPipTaskOrganizer.scheduleFinishResizePip(mBounds); + mPipTaskOrganizer.scheduleFinishResizePip(getBounds()); } mTemporaryBounds.setEmpty(); } @@ -604,7 +603,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, Log.d(TAG, "resizePipUnchecked: toBounds=" + toBounds + " callers=\n" + Debug.getCallers(5, " ")); } - if (!toBounds.equals(mBounds)) { + if (!toBounds.equals(getBounds())) { mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback); } } @@ -657,6 +656,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); - pw.println(innerPrefix + "mBounds=" + mBounds); + pw.println(innerPrefix + "mBounds=" + getBounds()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 8e9bf7434b3c..3468b888c06a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -38,9 +38,6 @@ import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; -import android.media.session.MediaController; -import android.media.session.MediaSessionManager; -import android.media.session.PlaybackState; import android.os.Debug; import android.os.Handler; import android.os.RemoteException; @@ -55,6 +52,7 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipTaskOrganizer; import java.util.ArrayList; @@ -110,22 +108,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private final PipBoundsState mPipBoundsState; private final PipBoundsHandler mPipBoundsHandler; private final PipTaskOrganizer mPipTaskOrganizer; + private final PipMediaController mPipMediaController; private IActivityTaskManager mActivityTaskManager; - private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP; private final Handler mHandler = new Handler(); private List<Listener> mListeners = new ArrayList<>(); - private List<MediaListener> mMediaListeners = new ArrayList<>(); private Rect mPipBounds; private Rect mDefaultPipBounds = new Rect(); private Rect mMenuModePipBounds; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private int mPipTaskId = TASK_ID_NO_PIP; private int mPinnedStackId = INVALID_STACK_ID; - private ComponentName mPipComponentName; - private MediaController mPipMediaController; private String[] mLastPackagesResourceGranted; private PipNotification mPipNotification; private ParceledListSlice<RemoteAction> mCustomActions; @@ -168,17 +163,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } }; - private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener = - controllers -> updateMediaController(controllers); + private final PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = new PipControllerPinnedStackListener(); @Override public void registerSessionListenerForCurrentUser() { - // TODO Need confirm if TV have to re-registers when switch user - mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener); - mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveMediaSessionListener, null, - UserHandle.USER_CURRENT, null); + mPipMediaController.registerSessionListenerForCurrentUser(); } /** @@ -232,12 +223,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsState pipBoundsState, PipBoundsHandler pipBoundsHandler, PipTaskOrganizer pipTaskOrganizer, - WindowManagerShellWrapper windowManagerShellWrapper - ) { + PipMediaController pipMediaController, + PipNotification pipNotification, + WindowManagerShellWrapper windowManagerShellWrapper) { mContext = context; mPipBoundsState = pipBoundsState; - mPipNotification = new PipNotification(context, this); + mPipNotification = pipNotification; mPipBoundsHandler = pipBoundsHandler; + mPipMediaController = pipMediaController; // Ensure that we have the display info in case we get calls to update the bounds // before the listener calls back final DisplayInfo displayInfo = new DisplayInfo(); @@ -250,6 +243,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); + addListener(mPipNotification); + final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ACTION_CLOSE); intentFilter.addAction(ACTION_MENU); @@ -261,7 +256,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mLastOrientation = initialConfig.orientation; loadConfigurationsAndApply(initialConfig); - mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class); mWindowManagerShellWrapper = windowManagerShellWrapper; try { mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); @@ -329,8 +323,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mState = STATE_NO_PIP; mPipTaskId = TASK_ID_NO_PIP; - mPipMediaController = null; - mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener); if (removePipStack) { try { mActivityTaskManager.removeTask(mPinnedStackId); @@ -371,13 +363,9 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo); mPinnedStackId = taskInfo.taskId; mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; - mPipComponentName = ComponentName.unflattenFromString( - taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]); // Set state to STATE_PIP so we show it when the pinned stack animation ends. mState = STATE_PIP; - mMediaSessionManager.addOnActiveSessionsChangedListener( - mActiveMediaSessionListener, null); - updateMediaController(mMediaSessionManager.getActiveSessions(null)); + mPipMediaController.onActivityPinned(); for (int i = mListeners.size() - 1; i >= 0; i--) { mListeners.get(i).onPipEntered(packageName); } @@ -554,20 +542,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } /** - * Adds a {@link MediaListener} to PipController. - */ - public void addMediaListener(MediaListener listener) { - mMediaListeners.add(listener); - } - - /** - * Removes a {@link MediaListener} from PipController. - */ - public void removeMediaListener(MediaListener listener) { - mMediaListeners.remove(listener); - } - - /** * Returns {@code true} if PIP is shown. */ public boolean isPipShown() { @@ -608,69 +582,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - private void updateMediaController(List<MediaController> controllers) { - MediaController mediaController = null; - if (controllers != null && getState() != STATE_NO_PIP && mPipComponentName != null) { - for (int i = controllers.size() - 1; i >= 0; i--) { - MediaController controller = controllers.get(i); - // We assumes that an app with PIPable activity - // keeps the single instance of media controller especially when PIP is on. - if (controller.getPackageName().equals(mPipComponentName.getPackageName())) { - mediaController = controller; - break; - } - } - } - if (mPipMediaController != mediaController) { - mPipMediaController = mediaController; - for (int i = mMediaListeners.size() - 1; i >= 0; i--) { - mMediaListeners.get(i).onMediaControllerChanged(); - } - if (mPipMediaController == null) { - mHandler.postDelayed(mClosePipRunnable, - CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS); - } else { - mHandler.removeCallbacks(mClosePipRunnable); - } - } - } - - /** - * Gets the {@link android.media.session.MediaController} for the PIPed activity. - */ - MediaController getMediaController() { - return mPipMediaController; - } - @Override public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) { - } - /** - * Returns the PIPed activity's playback state. - * This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED}, - * or {@link #PLAYBACK_STATE_UNAVAILABLE}. - */ - int getPlaybackState() { - if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) { - return PLAYBACK_STATE_UNAVAILABLE; - } - int state = mPipMediaController.getPlaybackState().getState(); - boolean isPlaying = (state == PlaybackState.STATE_BUFFERING - || state == PlaybackState.STATE_CONNECTING - || state == PlaybackState.STATE_PLAYING - || state == PlaybackState.STATE_FAST_FORWARDING - || state == PlaybackState.STATE_REWINDING - || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS - || state == PlaybackState.STATE_SKIPPING_TO_NEXT); - long actions = mPipMediaController.getPlaybackState().getActions(); - if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) { - return PLAYBACK_STATE_PAUSED; - } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) { - return PLAYBACK_STATE_PLAYING; - } - return PLAYBACK_STATE_UNAVAILABLE; + PipMediaController getPipMediaController() { + return mPipMediaController; } @Override @@ -718,14 +635,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac void onPipResizeAboutToStart(); } - /** - * A listener interface to receive change in PIP's media controller - */ - public interface MediaListener { - /** Invoked when the MediaController on PIPed activity is changed. */ - void onMediaControllerChanged(); - } - private String getStateDescription() { if (mSuspendPipResizingReason == 0) { return stateToName(mState); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java index 14960c38fd43..95d9b77c513e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java @@ -51,15 +51,11 @@ public class PipControlsView extends LinearLayout { setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); } - PipControlButtonView getFullButtonView() { + PipControlButtonView getFullscreenButton() { return findViewById(R.id.full_button); } - PipControlButtonView getCloseButtonView() { + PipControlButtonView getCloseButton() { return findViewById(R.id.close_button); } - - PipControlButtonView getPlayPauseButtonView() { - return findViewById(R.id.play_pause_button); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java index f66e9025a9ed..5265e7705ed9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java @@ -18,10 +18,10 @@ package com.android.wm.shell.pip.tv; import android.app.PendingIntent; import android.app.RemoteAction; +import android.content.Context; import android.graphics.Color; -import android.media.session.MediaController; -import android.media.session.PlaybackState; import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -29,9 +29,8 @@ import android.view.View; import com.android.wm.shell.R; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; /** @@ -42,213 +41,118 @@ public class PipControlsViewController { private static final float DISABLED_ACTION_ALPHA = 0.54f; - private final PipControlsView mView; - private final LayoutInflater mLayoutInflater; - private final Handler mHandler; private final PipController mPipController; - private final PipControlButtonView mPlayPauseButtonView; - private MediaController mMediaController; - private PipControlButtonView mFocusedChild; - private Listener mListener; - private ArrayList<PipControlButtonView> mCustomButtonViews = new ArrayList<>(); - private List<RemoteAction> mCustomActions = new ArrayList<>(); - - public PipControlsView getView() { - return mView; - } - - /** - * An interface to listen user action. - */ - public interface Listener { - /** - * Called when a user clicks close PIP button. - */ - void onClosed(); - } - private View.OnAttachStateChangeListener - mOnAttachStateChangeListener = - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - updateMediaController(); - mPipController.addMediaListener(mPipMediaListener); - } + private final Context mContext; + private final Handler mUiThreadHandler; + private final PipControlsView mView; + private final List<PipControlButtonView> mAdditionalButtons = new ArrayList<>(); - @Override - public void onViewDetachedFromWindow(View v) { - mPipController.removeMediaListener(mPipMediaListener); - } - }; + private final List<RemoteAction> mCustomActions = new ArrayList<>(); + private final List<RemoteAction> mMediaActions = new ArrayList<>(); - private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() { - @Override - public void onPlaybackStateChanged(PlaybackState state) { - updateUserActions(); - } - }; - - private final PipController.MediaListener mPipMediaListener = this::updateMediaController; - - private final View.OnFocusChangeListener - mFocusChangeListener = - new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if (hasFocus) { - mFocusedChild = (PipControlButtonView) view; - } else if (mFocusedChild == view) { - mFocusedChild = null; - } - } - }; - - public PipControlsViewController(PipControlsView view, PipController pipController, - LayoutInflater layoutInflater, Handler handler) { - super(); - mView = view; + public PipControlsViewController(PipControlsView view, PipController pipController) { + mContext = view.getContext(); + mUiThreadHandler = new Handler(Looper.getMainLooper()); mPipController = pipController; - mLayoutInflater = layoutInflater; - mHandler = handler; - - mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - if (mView.isAttachedToWindow()) { - mOnAttachStateChangeListener.onViewAttachedToWindow(mView); - } - - View fullButtonView = mView.getFullButtonView(); - fullButtonView.setOnFocusChangeListener(mFocusChangeListener); - fullButtonView.setOnClickListener(mView -> mPipController.movePipToFullscreen()); - - View closeButtonView = mView.getCloseButtonView(); - closeButtonView.setOnFocusChangeListener(mFocusChangeListener); - closeButtonView.setOnClickListener(v -> { - mPipController.closePip(); - if (mListener != null) { - mListener.onClosed(); - } - }); + mView = view; - mPlayPauseButtonView = mView.getPlayPauseButtonView(); - mPlayPauseButtonView.setOnFocusChangeListener(mFocusChangeListener); - mPlayPauseButtonView.setOnClickListener(v -> { - if (mMediaController == null || mMediaController.getPlaybackState() == null) { - return; - } - final int playbackState = mPipController.getPlaybackState(); - if (playbackState == PipController.PLAYBACK_STATE_PAUSED) { - mMediaController.getTransportControls().play(); - } else if (playbackState == PipController.PLAYBACK_STATE_PLAYING) { - mMediaController.getTransportControls().pause(); - } + mView.getFullscreenButton().setOnClickListener(v -> mPipController.movePipToFullscreen()); + mView.getCloseButton().setOnClickListener(v -> mPipController.closePip()); - // View will be updated later in {@link mMediaControllerCallback} - }); + mPipController.getPipMediaController().addActionListener(this::onMediaActionsChanged); } - private void updateMediaController() { - AtomicReference<MediaController> newController = new AtomicReference<>(); - newController.set(mPipController.getMediaController()); + PipControlsView getView() { + return mView; + } - if (newController.get() == null || mMediaController == newController.get()) { + /** + * Updates the set of activity-defined actions. + */ + void setCustomActions(List<? extends RemoteAction> actions) { + if (mCustomActions.isEmpty() && actions.isEmpty()) { + // Nothing changed - return early. return; } - if (mMediaController != null) { - mMediaController.unregisterCallback(mMediaControllerCallback); + mCustomActions.clear(); + mCustomActions.addAll(actions); + updateAdditionalActions(); + } + + private void onMediaActionsChanged(List<RemoteAction> actions) { + if (mMediaActions.isEmpty() && actions.isEmpty()) { + // Nothing changed - return early. + return; } - mMediaController = newController.get(); - if (mMediaController != null) { - mMediaController.registerCallback(mMediaControllerCallback); + mMediaActions.clear(); + mMediaActions.addAll(actions); + + // Update the view only if there are no custom actions (media actions are only shown when + // there no custom actions). + if (mCustomActions.isEmpty()) { + updateAdditionalActions(); } - updateUserActions(); } - /** - * Updates the actions for the PIP. If there are no custom actions, then the media session - * actions are shown. - */ - private void updateUserActions() { + private void updateAdditionalActions() { + final List<RemoteAction> actionsToDisplay; if (!mCustomActions.isEmpty()) { - // Ensure we have as many buttons as actions - while (mCustomButtonViews.size() < mCustomActions.size()) { - PipControlButtonView buttonView = (PipControlButtonView) mLayoutInflater.inflate( + // If there are custom actions: show them. + actionsToDisplay = mCustomActions; + } else if (!mMediaActions.isEmpty()) { + // If there are no custom actions, but there media actions: show them. + actionsToDisplay = mMediaActions; + } else { + // If there no custom actions and no media actions: clean up all the additional buttons. + actionsToDisplay = Collections.emptyList(); + } + + // Make sure we exactly as many additional buttons as we have actions to display. + final int actionsNumber = actionsToDisplay.size(); + int buttonsNumber = mAdditionalButtons.size(); + if (actionsNumber > buttonsNumber) { + final LayoutInflater layoutInflater = LayoutInflater.from(mContext); + // Add buttons until we have enough to display all of the actions. + while (actionsNumber > buttonsNumber) { + final PipControlButtonView button = (PipControlButtonView) layoutInflater.inflate( R.layout.tv_pip_custom_control, mView, false); - mView.addView(buttonView); - mCustomButtonViews.add(buttonView); - } + mView.addView(button); + mAdditionalButtons.add(button); - // Update the visibility of all views - for (int i = 0; i < mCustomButtonViews.size(); i++) { - mCustomButtonViews.get(i).setVisibility( - i < mCustomActions.size() ? View.VISIBLE : View.GONE); + buttonsNumber++; } - - // Update the state and visibility of the action buttons, and hide the rest - for (int i = 0; i < mCustomActions.size(); i++) { - final RemoteAction action = mCustomActions.get(i); - PipControlButtonView actionView = mCustomButtonViews.get(i); - - // TODO: Check if the action drawable has changed before we reload it - action.getIcon().loadDrawableAsync(mView.getContext(), d -> { - d.setTint(Color.WHITE); - actionView.setImageDrawable(d); - }, mHandler); - actionView.setText(action.getContentDescription()); - if (action.isEnabled()) { - actionView.setOnClickListener(v -> { - try { - action.getActionIntent().send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send action", e); - } - }); - } - actionView.setEnabled(action.isEnabled()); - actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); + } else if (actionsNumber < buttonsNumber) { + // Hide buttons until we as many as the actions. + while (actionsNumber < buttonsNumber) { + final View button = mAdditionalButtons.get(buttonsNumber - 1); + button.setVisibility(View.GONE); + button.setOnClickListener(null); + + buttonsNumber--; } + } - // Hide the media session buttons - mPlayPauseButtonView.setVisibility(View.GONE); - } else { - AtomicInteger state = new AtomicInteger(PipController.STATE_UNKNOWN); - state.set(mPipController.getPlaybackState()); - if (state.get() == PipController.STATE_UNKNOWN - || state.get() == PipController.PLAYBACK_STATE_UNAVAILABLE) { - mPlayPauseButtonView.setVisibility(View.GONE); - } else { - mPlayPauseButtonView.setVisibility(View.VISIBLE); - if (state.get() == PipController.PLAYBACK_STATE_PLAYING) { - mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_pause_white); - mPlayPauseButtonView.setText(R.string.pip_pause); - } else { - mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_play_arrow_white); - mPlayPauseButtonView.setText(R.string.pip_play); + // "Assign" actions to the buttons. + for (int index = 0; index < actionsNumber; index++) { + final RemoteAction action = actionsToDisplay.get(index); + final PipControlButtonView button = mAdditionalButtons.get(index); + button.setVisibility(View.VISIBLE); // Ensure the button is visible. + button.setText(action.getContentDescription()); + button.setEnabled(action.isEnabled()); + button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); + button.setOnClickListener(v -> { + try { + action.getActionIntent().send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to send action", e); } - } + }); - // Hide all the custom action buttons - for (int i = 0; i < mCustomButtonViews.size(); i++) { - mCustomButtonViews.get(i).setVisibility(View.GONE); - } + action.getIcon().loadDrawableAsync(mContext, drawable -> { + drawable.setTint(Color.WHITE); + button.setImageDrawable(drawable); + }, mUiThreadHandler); } } - - - /** - * Sets the {@link Listener} to listen user actions. - */ - public void setListener(Listener listener) { - mListener = listener; - } - - - /** - * Updates the set of activity-defined actions. - */ - public void setActions(List<? extends RemoteAction> actions) { - mCustomActions.clear(); - mCustomActions.addAll(actions); - updateUserActions(); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java index e185a9604449..d2270c278161 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java @@ -56,8 +56,7 @@ public class PipMenuActivity extends Activity implements PipController.Listener } setContentView(R.layout.tv_pip_menu); mPipControlsViewController = new PipControlsViewController( - findViewById(R.id.pip_controls), sPipController, - getLayoutInflater(), getApplicationContext().getMainThreadHandler()); + findViewById(R.id.pip_controls), sPipController); sPipController.addListener(this); mRestorePipSizeWhenClose = true; mFadeInAnimation = AnimatorInflater.loadAnimator( @@ -141,7 +140,7 @@ public class PipMenuActivity extends Activity implements PipController.Listener if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()"); boolean hasCustomActions = actions != null && !actions.getList().isEmpty(); - mPipControlsViewController.setActions( + mPipControlsViewController.setCustomActions( hasCustomActions ? actions.getList() : Collections.emptyList()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java index f5bbd23fa1d6..b30dee4f331f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java @@ -28,36 +28,33 @@ import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Bitmap; import android.media.MediaMetadata; -import android.media.session.MediaController; -import android.media.session.PlaybackState; import android.text.TextUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.wm.shell.R; +import com.android.wm.shell.pip.PipMediaController; + +import java.util.Objects; /** * A notification that informs users that PIP is running and also provides PIP controls. * <p>Once it's created, it will manage the PIP notification UI by itself except for handling * configuration changes. */ -public class PipNotification { +public class PipNotification implements PipController.Listener { + private static final boolean DEBUG = PipController.DEBUG; private static final String TAG = "PipNotification"; + private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName(); - private static final boolean DEBUG = PipController.DEBUG; + public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP"; static final String ACTION_MENU = "PipNotification.menu"; static final String ACTION_CLOSE = "PipNotification.close"; - public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP"; - private final PackageManager mPackageManager; - - private final PipController mPipController; - private final NotificationManager mNotificationManager; private final Notification.Builder mNotificationBuilder; - private MediaController mMediaController; private String mDefaultTitle; private int mDefaultIconResId; @@ -67,87 +64,9 @@ public class PipNotification { private String mMediaTitle; private Bitmap mArt; - private PipController.Listener mPipListener = new PipController.Listener() { - @Override - public void onPipEntered(String packageName) { - mPackageName = packageName; - updateMediaControllerMetadata(); - notifyPipNotification(); - } - - @Override - public void onPipActivityClosed() { - dismissPipNotification(); - mPackageName = null; - } - - @Override - public void onShowPipMenu() { - // no-op. - } - - @Override - public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) { - // no-op. - } - - @Override - public void onMoveToFullscreen() { - dismissPipNotification(); - mPackageName = null; - } - - @Override - public void onPipResizeAboutToStart() { - // no-op. - } - }; - - private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() { - @Override - public void onPlaybackStateChanged(PlaybackState state) { - if (updateMediaControllerMetadata() && mNotified) { - // update notification - notifyPipNotification(); - } - } - - @Override - public void onMetadataChanged(MediaMetadata metadata) { - if (updateMediaControllerMetadata() && mNotified) { - // update notification - notifyPipNotification(); - } - } - }; - - private final PipController.MediaListener mPipMediaListener = - new PipController.MediaListener() { - @Override - public void onMediaControllerChanged() { - MediaController newController = mPipController.getMediaController(); - if (newController == null || mMediaController == newController) { - return; - } - if (mMediaController != null) { - mMediaController.unregisterCallback(mMediaControllerCallback); - } - mMediaController = newController; - if (mMediaController != null) { - mMediaController.registerCallback(mMediaControllerCallback); - } - if (updateMediaControllerMetadata() && mNotified) { - // update notification - notifyPipNotification(); - } - } - }; - - public PipNotification(Context context, PipController pipController) { + public PipNotification(Context context, PipMediaController pipMediaController) { mPackageManager = context.getPackageManager(); - - mNotificationManager = (NotificationManager) context.getSystemService( - Context.NOTIFICATION_SERVICE); + mNotificationManager = context.getSystemService(NotificationManager.class); mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL_TVPIP) .setLocalOnly(true) @@ -157,13 +76,51 @@ public class PipNotification { .setContentIntent(createPendingIntent(context, ACTION_MENU)) .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE))); - mPipController = pipController; - pipController.addListener(mPipListener); - pipController.addMediaListener(mPipMediaListener); + pipMediaController.addMetadataListener(this::onMediaMetadataChanged); onConfigurationChanged(context); } + @Override + public void onPipEntered(String packageName) { + mPackageName = packageName; + notifyPipNotification(); + } + + @Override + public void onPipActivityClosed() { + dismissPipNotification(); + mPackageName = null; + } + + @Override + public void onShowPipMenu() { + // no-op. + } + + @Override + public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) { + // no-op. + } + + @Override + public void onMoveToFullscreen() { + dismissPipNotification(); + mPackageName = null; + } + + @Override + public void onPipResizeAboutToStart() { + // no-op. + } + + private void onMediaMetadataChanged(MediaMetadata metadata) { + if (updateMediaControllerMetadata(metadata) && mNotified) { + // update notification + notifyPipNotification(); + } + } + /** * Called by {@link PipController} when the configuration is changed. */ @@ -199,28 +156,28 @@ public class PipNotification { mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); } - private boolean updateMediaControllerMetadata() { + private boolean updateMediaControllerMetadata(MediaMetadata metadata) { String title = null; Bitmap art = null; - if (mPipController.getMediaController() != null) { - MediaMetadata metadata = mPipController.getMediaController().getMetadata(); - if (metadata != null) { - title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE); - if (TextUtils.isEmpty(title)) { - title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE); - } - art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); - if (art == null) { - art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART); - } + if (metadata != null) { + title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE); + if (TextUtils.isEmpty(title)) { + title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE); + } + art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + if (art == null) { + art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART); } } - if (!TextUtils.equals(title, mMediaTitle) || art != mArt) { - mMediaTitle = title; - mArt = art; - return true; + + if (TextUtils.equals(title, mMediaTitle) && Objects.equals(art, mArt)) { + return false; } - return false; + + mMediaTitle = title; + mArt = art; + + return true; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 39a96a291c86..1d10a84c53b9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -69,11 +69,13 @@ public class PipTaskOrganizerTest extends PipTestCase { private PipBoundsState mPipBoundsState; private ComponentName mComponent1; + private ComponentName mComponent2; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mComponent1 = new ComponentName(mContext, "component1"); + mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, @@ -101,29 +103,57 @@ public class PipTaskOrganizerTest extends PipTestCase { } @Test + public void startSwipePipToHome_updatesLastPipComponentName() { + mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, null); + + assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName()); + } + + @Test public void onTaskAppeared_updatesAspectRatio() { final Rational aspectRatio = new Rational(2, 1); - mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo( + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(aspectRatio)), null /* leash */); assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); } @Test + public void onTaskAppeared_updatesLastPipComponentName() { + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)), + null /* leash */); + + assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName()); + } + + @Test public void onTaskInfoChanged_updatesAspectRatioIfChanged() { final Rational startAspectRatio = new Rational(2, 1); final Rational newAspectRatio = new Rational(1, 2); - mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo( + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(startAspectRatio)), null /* leash */); - mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(createPipParams(newAspectRatio))); + mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1, + createPipParams(newAspectRatio))); assertEquals(newAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); } + @Test + public void onTaskInfoChanged_updatesLastPipComponentName() { + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, + createPipParams(null)), null /* leash */); + + mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2, + createPipParams(null))); + + assertEquals(mComponent2, mPipBoundsState.getLastPipComponentName()); + } + private void preparePipTaskOrg() { final DisplayInfo info = new DisplayInfo(); + mPipBoundsState.setDisplayInfo(info); when(mMockPipBoundsHandler.getDestinationBounds(any(), any())).thenReturn(new Rect()); when(mMockPipBoundsHandler.getDestinationBounds(any(), any(), anyBoolean())) .thenReturn(new Rect()); @@ -133,10 +163,12 @@ public class PipTaskOrganizerTest extends PipTestCase { doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any()); } - private static ActivityManager.RunningTaskInfo createTaskInfo(PictureInPictureParams params) { + private static ActivityManager.RunningTaskInfo createTaskInfo( + ComponentName componentName, PictureInPictureParams params) { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.token = mock(WindowContainerToken.class); info.pictureInPictureParams = params; + info.topActivity = componentName; return info; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index de9d8648f43c..5f0f1964814f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -20,11 +20,14 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; @@ -34,6 +37,7 @@ import android.testing.TestableLooper; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; @@ -64,6 +68,7 @@ public class PipControllerTest extends PipTestCase { @Mock private PipTouchHandler mMockPipTouchHandler; @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper; @Mock private PipBoundsState mMockPipBoundsState; + @Mock private ShellExecutor mMockExecutor; @Before public void setUp() throws RemoteException { @@ -71,7 +76,11 @@ public class PipControllerTest extends PipTestCase { mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper); + mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mMockExecutor).execute(any()); } @Test @@ -99,6 +108,27 @@ public class PipControllerTest extends PipTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper)); + mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor)); + } + + @Test + public void onActivityHidden_isLastPipComponentName_clearLastPipComponent() { + final ComponentName component1 = new ComponentName(mContext, "component1"); + when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); + + mPipController.mPinnedStackListener.onActivityHidden(component1); + + verify(mMockPipBoundsState).setLastPipComponentName(null); + } + + @Test + public void onActivityHidden_isNotLastPipComponentName_lastPipComponentNotCleared() { + final ComponentName component1 = new ComponentName(mContext, "component1"); + final ComponentName component2 = new ComponentName(mContext, "component2"); + when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); + + mPipController.mPinnedStackListener.onActivityHidden(component2); + + verify(mMockPipBoundsState, never()).setLastPipComponentName(null); } } diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index 9d83e491fdc1..3a99d41448f2 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -7,6 +7,8 @@ cc_library_host_static { static_libs: [ "libbase", + "libmath", + "libutils", ], srcs: [ @@ -24,6 +26,7 @@ cc_library_host_static { "frameworks/native/libs/nativebase/include", "frameworks/native/libs/nativewindow/include", "frameworks/native/libs/arect/include", + "frameworks/native/libs/ui/include_private", ], export_include_dirs: ["."], diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index cd2af1b85f50..bd46ffdf7e57 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -901,8 +901,59 @@ public final class GnssMeasurement implements Parcelable { /** * Gets 'Accumulated Delta Range' state. * - * <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a - * cycle slip (indicating 'loss of lock'). + * <p>This indicates the state of the {@link #getAccumulatedDeltaRangeMeters()} measurement. See + * the table below for a detailed interpretation of each state. + * + * <table border="1"> + * <thead> + * <tr> + * <th>ADR_STATE</th> + * <th>Time of relevance</th> + * <th>Interpretation</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td>UNKNOWN</td> + * <td>ADR(t)</td> + * <td>No valid carrier phase information is available at time t.</td> + * </tr> + * <tr> + * <td>VALID</td> + * <td>ADR(t)</td> + * <td>Valid carrier phase information is available at time t. This indicates that this + * measurement can be used as a reference for future measurements. However, to compare it to + * previous measurements to compute delta range, other bits should be checked. Specifically, + * it can be used for delta range computation if it is valid and has no reset or cycle slip at + * this epoch i.e. if VALID_BIT == 1 && CYCLE_SLIP_BIT == 0 && RESET_BIT == 0.</td> + * </tr> + * <tr> + * <td>RESET</td> + * <td>ADR(t) - ADR(t-1)</td> + * <td>Carrier phase accumulation has been restarted between current time t and previous time + * t-1. This indicates that this measurement can be used as a reference for future measurements, + * but it should not be compared to previous measurements to compute delta range.</td> + * </tr> + * <tr> + * <td>CYCLE_SLIP</td> + * <td>ADR(t) - ADR(t-1)</td> + * <td>Cycle slip(s) have been detected between the current time t and previous time t-1. This + * indicates that this measurement can be used as a reference for future measurements. Clients + * can use a measurement with a cycle slip to compute delta range against previous measurements + * at their own risk.</td> + * </tr> + * <tr> + * <td>HALF_CYCLE_RESOLVED</td> + * <td>ADR(t)</td> + * <td>Half cycle ambiguity is resolved at time t.</td> + * </tr> + * <tr> + * <td>HALF_CYCLE_REPORTED</td> + * <td>ADR(t)</td> + * <td>Half cycle ambiguity is reported at time t.</td> + * </tr> + * </tbody> + * </table> */ @AdrState public int getAccumulatedDeltaRangeState() { diff --git a/media/java/android/media/ApplicationMediaCapabilities.java b/media/java/android/media/ApplicationMediaCapabilities.java new file mode 100644 index 000000000000..4b28553cce90 --- /dev/null +++ b/media/java/android/media/ApplicationMediaCapabilities.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.NonNull; +import android.content.ContentResolver; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * ApplicationMediaCapabilities is an immutable class that encapsulates an application's + * capabilities of handling advanced media formats. + * + * The ApplicationMediaCapabilities class is used by the platform to to represent an application's + * media capabilities as defined in their manifest(TODO: Add link) in order to determine + * whether modern media files need to be transcoded for that application (TODO: Add link). + * + * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with + * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more + * control over the transcoding that is built into the platform. ApplicationMediaCapabilities + * provided by applications at runtime like this override the default manifest capabilities for that + * media access. + * + * TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added. + * TODO(hkuang): Add a link to seamless transcoding detail when it is published + * TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList + * + * @hide + */ +public final class ApplicationMediaCapabilities implements Parcelable { + private static final String TAG = "ApplicationMediaCapabilities"; + + /** Whether handling of HEVC video is supported. */ + private final boolean mIsHevcSupported; + + /** Whether handling of slow-motion video is supported. */ + private final boolean mIsSlowMotionSupported; + + /** Whether handling of high dynamic range video is supported. */ + private final boolean mIsHdrSupported; + + private ApplicationMediaCapabilities(Builder b) { + mIsHevcSupported = b.mIsHevcSupported; + mIsHdrSupported = b.mIsHdrSupported; + mIsSlowMotionSupported = b.mIsSlowMotionSupported; + } + + /** Whether handling of HEVC video is supported. */ + public boolean isHevcSupported() { + return mIsHevcSupported; + } + + /** Whether handling of slow-motion video is supported. */ + public boolean isSlowMotionSupported() { + return mIsSlowMotionSupported; + } + + /** Whether handling of high dynamic range video is supported. */ + public boolean isHdrSupported() { + return mIsHdrSupported; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(mIsHevcSupported); + dest.writeBoolean(mIsHdrSupported); + dest.writeBoolean(mIsSlowMotionSupported); + } + + /** + * Builder class for {@link ApplicationMediaCapabilities} objects. + * Use this class to configure and create an ApplicationMediaCapabilities instance. Builder + * could be created from an existing ApplicationMediaCapabilities object, from a xml file or + * MediaCodecList. + * //TODO(hkuang): Add xml parsing support to the builder. + */ + public final static class Builder { + private boolean mIsHevcSupported = false; + private boolean mIsHdrSupported = false; + private boolean mIsSlowMotionSupported = false; + + /** + * Constructs a new Builder with all the supports default to false. + */ + public Builder() { + } + + /** + * Builds a {@link ApplicationMediaCapabilities} object. + * + * @return a new {@link ApplicationMediaCapabilities} instance successfully initialized + * with all the parameters set on this <code>Builder</code>. + * @throws UnsupportedOperationException if the parameters set on the + * <code>Builder</code> were incompatible, or if they are not supported by the + * device. + */ + @NonNull + public ApplicationMediaCapabilities build() { + if (mIsHdrSupported && !mIsHevcSupported) { + throw new UnsupportedOperationException("Must also support HEVC if support HDR."); + } + return new ApplicationMediaCapabilities(this); + } + + /** + * Sets whether supports HEVC encoded video. + */ + @NonNull + public Builder setHevcSupported(boolean hevcSupported) { + mIsHevcSupported = hevcSupported; + return this; + } + + /** + * Sets whether supports high dynamic range video. + */ + @NonNull + public Builder setHdrSupported(boolean hdrSupported) { + mIsHdrSupported = hdrSupported; + return this; + } + + /** + * Sets whether supports slow-motion video. + */ + @NonNull + public Builder setSlowMotionSupported(boolean slowMotionSupported) { + mIsSlowMotionSupported = slowMotionSupported; + return this; + } + } +} diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java new file mode 100644 index 000000000000..8a668c670d1e --- /dev/null +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediatranscodingtest; + +/* + * Test for ApplicationMediaCapabilities in the media framework. + * + * To run this test suite: + make frameworks/base/media/tests/MediaTranscodingTest + make mediatranscodingtest + + adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk + + adb shell am instrument -e class \ + com.android.mediatranscodingtest.MediaCapabilityTest \ + -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner + * + */ + +import static org.testng.Assert.assertThrows; + +import android.media.ApplicationMediaCapabilities; +import android.test.ActivityInstrumentationTestCase2; + +import org.junit.Test; + +public class ApplicationMediaCapabilitiesTest extends + ActivityInstrumentationTestCase2<MediaTranscodingTest> { + private static final String TAG = "MediaCapabilityTest"; + + public ApplicationMediaCapabilitiesTest() { + super("com.android.MediaCapabilityTest", MediaTranscodingTest.class); + } + + @Test + public void testSetSupportHevc() throws Exception { + ApplicationMediaCapabilities capability = + new ApplicationMediaCapabilities.Builder().setHevcSupported(true).build(); + assertTrue(capability.isHevcSupported()); + + ApplicationMediaCapabilities capability2 = + new ApplicationMediaCapabilities.Builder().setHevcSupported(false).build(); + assertFalse(capability2.isHevcSupported()); + } + + @Test + public void testSetSupportHdr() throws Exception { + ApplicationMediaCapabilities capability = + new ApplicationMediaCapabilities.Builder().setHdrSupported(true).setHevcSupported( + true).build(); + assertEquals(true, capability.isHdrSupported()); + } + + @Test + public void testSetSupportSlowMotion() throws Exception { + ApplicationMediaCapabilities capability = + new ApplicationMediaCapabilities.Builder().setSlowMotionSupported( + true).build(); + assertTrue(capability.isSlowMotionSupported()); + } + + @Test + public void testBuilder() throws Exception { + ApplicationMediaCapabilities capability = + new ApplicationMediaCapabilities.Builder().setHdrSupported( + true).setHevcSupported(true).setSlowMotionSupported(true).build(); + assertTrue(capability.isHdrSupported()); + assertTrue(capability.isSlowMotionSupported()); + assertTrue(capability.isSlowMotionSupported()); + } + + @Test + public void testSupportHdrWithoutSupportHevc() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + ApplicationMediaCapabilities capability = + new ApplicationMediaCapabilities.Builder().setHdrSupported( + true).setHevcSupported(false).build(); + }); + } +} diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java index bd1551f352f4..76050ac4aba1 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java @@ -37,6 +37,7 @@ public class MediaTranscodingTestRunner extends InstrumentationTestRunner { public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(MediaTranscodeManagerDiedTest.class); + suite.addTestSuite(ApplicationMediaCapabilitiesTest.class); suite.addTestSuite(MediaTranscodeManagerTest.class); suite.addTestSuite(MediaTranscodingBenchmark.class); return suite; diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 41a9057c005a..16a5f5e82f21 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -42276,13 +42276,13 @@ package android.service.notification { method public boolean canBubble(); method public boolean canShowBadge(); method public android.app.NotificationChannel getChannel(); + method @Nullable public android.content.pm.ShortcutInfo getConversationShortcutInfo(); method public int getImportance(); method public CharSequence getImportanceExplanation(); method public String getKey(); method public long getLastAudiblyAlertedMillis(); method public String getOverrideGroupKey(); method public int getRank(); - method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo(); method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions(); method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies(); method public int getSuppressedVisualEffects(); @@ -55346,8 +55346,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -55573,8 +55573,8 @@ package android.view.inputmethod { method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -55608,8 +55608,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index ae6ecc763aef..06e6d9ccbbf0 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -130,6 +130,7 @@ package android.provider { public final class DeviceConfig { field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager"; + field public static final String NAMESPACE_APP_STANDBY = "app_standby"; field public static final String NAMESPACE_DEVICE_IDLE = "device_idle"; } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 9f9658308838..eea50be6da1d 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -682,7 +682,7 @@ package android.app { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; @@ -2142,6 +2142,7 @@ package android.content.pm { method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String); method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo); method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean); + method public void setSystemAppState(@NonNull String, int); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean); method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int); method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); @@ -2220,11 +2221,16 @@ package android.content.pm { field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 + field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 field public static final int MODULE_APEX_NAME = 1; // 0x1 field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 field public static final int RESTRICTION_NONE = 0; // 0x0 + field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0 + field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1 + field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2 + field public static final int SYSTEM_APP_STATE_UNINSTALLED = 3; // 0x3 } public abstract static class PackageManager.DexModuleRegisterCallback { @@ -8883,6 +8889,7 @@ package android.service.autofill { public static final class Dataset.Builder { ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData); method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); } @@ -10887,7 +10894,8 @@ package android.telephony.data { method public int getMtuV6(); method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses(); method public int getProtocolType(); - method public int getSuggestedRetryTime(); + method public long getRetryIntervalMillis(); + method @Deprecated public int getSuggestedRetryTime(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1 @@ -10899,6 +10907,7 @@ package android.telephony.data { field public static final int LINK_STATUS_DORMANT = 1; // 0x1 field public static final int LINK_STATUS_INACTIVE = 0; // 0x0 field public static final int LINK_STATUS_UNKNOWN = -1; // 0xffffffff + field public static final int RETRY_INTERVAL_UNDEFINED = -1; // 0xffffffff } public static final class DataCallResponse.Builder { @@ -10917,7 +10926,8 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>); method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int); - method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryIntervalMillis(long); + method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); } public final class DataProfile implements android.os.Parcelable { diff --git a/packages/CarSystemUI/res/values-ar/strings.xml b/packages/CarSystemUI/res/values-ar/strings.xml index 61a08a4eae49..d9abb0af5608 100644 --- a/packages/CarSystemUI/res/values-ar/strings.xml +++ b/packages/CarSystemUI/res/values-ar/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"يمكن لأي مستخدم تحديث التطبيقات لجميع المستخدمين الآخرين."</string> <string name="car_loading_profile" msgid="4507385037552574474">"جارٍ التحميل"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"جارٍ تحميل الملف الشخصي الجديد للمستخدم (من <xliff:g id="FROM_USER">%1$d</xliff:g> إلى <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"إغلاق"</string> </resources> diff --git a/packages/CarSystemUI/res/values-bn/strings.xml b/packages/CarSystemUI/res/values-bn/strings.xml index a45e66e8c2f2..5664cc12e109 100644 --- a/packages/CarSystemUI/res/values-bn/strings.xml +++ b/packages/CarSystemUI/res/values-bn/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"যেকোনও ব্যবহারকারী বাকি সব ব্যবহারকারীর জন্য অ্যাপ আপডেট করতে পারবেন।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"লোড হচ্ছে"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যবহারকারীর প্রোফাইল লোড করা হচ্ছে (<xliff:g id="FROM_USER">%1$d</xliff:g> থেকে <xliff:g id="TO_USER">%2$d</xliff:g>-এ)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"বন্ধ করুন"</string> </resources> diff --git a/packages/CarSystemUI/res/values-de/strings.xml b/packages/CarSystemUI/res/values-de/strings.xml index 131dee19b0ca..e2437f0c55cb 100644 --- a/packages/CarSystemUI/res/values-de/strings.xml +++ b/packages/CarSystemUI/res/values-de/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Jeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Wird geladen"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nutzer wird geladen (von <xliff:g id="FROM_USER">%1$d</xliff:g> bis <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Schließen"</string> </resources> diff --git a/packages/CarSystemUI/res/values-eu/strings.xml b/packages/CarSystemUI/res/values-eu/strings.xml index 5d2ca3548af2..9139e650187e 100644 --- a/packages/CarSystemUI/res/values-eu/strings.xml +++ b/packages/CarSystemUI/res/values-eu/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Edozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Kargatzen"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Erabiltzailea kargatzen (<xliff:g id="FROM_USER">%1$d</xliff:g> izatetik<xliff:g id="TO_USER">%2$d</xliff:g> izatera igaroko da)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Itxi"</string> </resources> diff --git a/packages/CarSystemUI/res/values-gl/strings.xml b/packages/CarSystemUI/res/values-gl/strings.xml index e4586cc17a73..e77df4f50272 100644 --- a/packages/CarSystemUI/res/values-gl/strings.xml +++ b/packages/CarSystemUI/res/values-gl/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Calquera usuario pode actualizar as aplicacións para o resto dos usuarios."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (do <xliff:g id="FROM_USER">%1$d</xliff:g> ao <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Pechar"</string> </resources> diff --git a/packages/CarSystemUI/res/values-gu/strings.xml b/packages/CarSystemUI/res/values-gu/strings.xml index ba884e410187..174d7a724240 100644 --- a/packages/CarSystemUI/res/values-gu/strings.xml +++ b/packages/CarSystemUI/res/values-gu/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"કોઈપણ વપરાશકર્તા અન્ય બધા વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string> <string name="car_loading_profile" msgid="4507385037552574474">"લોડ કરી રહ્યાં છીએ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"વપરાશકર્તાને લોડ કરી રહ્યાં છીએ (<xliff:g id="FROM_USER">%1$d</xliff:g>માંથી <xliff:g id="TO_USER">%2$d</xliff:g>માં)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"બંધ કરો"</string> </resources> diff --git a/packages/CarSystemUI/res/values-kn/strings.xml b/packages/CarSystemUI/res/values-kn/strings.xml index 34a83553d5c0..b9667df3afb3 100644 --- a/packages/CarSystemUI/res/values-kn/strings.xml +++ b/packages/CarSystemUI/res/values-kn/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಆ್ಯಪ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು."</string> <string name="car_loading_profile" msgid="4507385037552574474">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ಬಳಕೆದಾರರನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ (<xliff:g id="FROM_USER">%1$d</xliff:g> ನಿಂದ <xliff:g id="TO_USER">%2$d</xliff:g> ವರೆಗೆ)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ಮುಚ್ಚಿರಿ"</string> </resources> diff --git a/packages/CarSystemUI/res/values-ml/strings.xml b/packages/CarSystemUI/res/values-ml/strings.xml index 3bc7557b5484..613ea59874bc 100644 --- a/packages/CarSystemUI/res/values-ml/strings.xml +++ b/packages/CarSystemUI/res/values-ml/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ഏതൊരു ഉപയോക്താവിനും മറ്റെല്ലാ ഉപയോക്താക്കൾക്കുമായി ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്യാനാവും."</string> <string name="car_loading_profile" msgid="4507385037552574474">"ലോഡ് ചെയ്യുന്നു"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ഉപയോക്തൃ പ്രൊഫൈൽ ലോഡ് ചെയ്യുന്നു (<xliff:g id="FROM_USER">%1$d</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="TO_USER">%2$d</xliff:g> എന്നതിലേക്ക്)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"അടയ്ക്കുക"</string> </resources> diff --git a/packages/CarSystemUI/res/values-mr/strings.xml b/packages/CarSystemUI/res/values-mr/strings.xml index fdbab4fbc4f2..b1c8a72a04df 100644 --- a/packages/CarSystemUI/res/values-mr/strings.xml +++ b/packages/CarSystemUI/res/values-mr/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"कोणताही वापरकर्ता इतर सर्व वापरकर्त्यांसाठी अॅप्स अपडेट करू शकतो."</string> <string name="car_loading_profile" msgid="4507385037552574474">"लोड करत आहे"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"वापरकर्ता लोड करत आहे (<xliff:g id="FROM_USER">%1$d</xliff:g> पासून <xliff:g id="TO_USER">%2$d</xliff:g> पर्यंत)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"बंद करा"</string> </resources> diff --git a/packages/CarSystemUI/res/values-ne/strings.xml b/packages/CarSystemUI/res/values-ne/strings.xml index e9eb4d8bdac7..3a25d6e05db6 100644 --- a/packages/CarSystemUI/res/values-ne/strings.xml +++ b/packages/CarSystemUI/res/values-ne/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"सबै प्रयोगकर्ताले अन्य प्रयोगकर्ताका अनुप्रयोगहरू अद्यावधिक गर्न सक्छन्।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"लोड गरिँदै"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"प्रयोगकर्तासम्बन्धी जानकारी लोड गरिँदै (<xliff:g id="FROM_USER">%1$d</xliff:g> बाट <xliff:g id="TO_USER">%2$d</xliff:g> मा)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"बन्द गर्नुहोस्"</string> </resources> diff --git a/packages/CarSystemUI/res/values-or/strings.xml b/packages/CarSystemUI/res/values-or/strings.xml index 58f59e4c4dbf..4168d5a109b8 100644 --- a/packages/CarSystemUI/res/values-or/strings.xml +++ b/packages/CarSystemUI/res/values-or/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ଯେ କୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଅନ୍ୟ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଆପଗୁଡ଼ିକୁ ଅପଡେଟ୍ କରିପାରିବେ।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"ଲୋଡ୍ କରାଯାଉଛି"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଲୋଡ୍ କରାଯାଉଛି (<xliff:g id="FROM_USER">%1$d</xliff:g>ଙ୍କ ଠାରୁ <xliff:g id="TO_USER">%2$d</xliff:g> ପର୍ଯ୍ୟନ୍ତ)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ବନ୍ଦ କରନ୍ତୁ"</string> </resources> diff --git a/packages/CarSystemUI/res/values-pa/strings.xml b/packages/CarSystemUI/res/values-pa/strings.xml index e73e20a5fc85..3d9d3a597c07 100644 --- a/packages/CarSystemUI/res/values-pa/strings.xml +++ b/packages/CarSystemUI/res/values-pa/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ਕੋਈ ਵੀ ਵਰਤੋਂਕਾਰ ਹੋਰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਸਕਦਾ ਹੈ।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ (<xliff:g id="FROM_USER">%1$d</xliff:g> ਤੋਂ <xliff:g id="TO_USER">%2$d</xliff:g> ਤੱਕ)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ਬੰਦ ਕਰੋ"</string> </resources> diff --git a/packages/CarSystemUI/res/values-te/strings.xml b/packages/CarSystemUI/res/values-te/strings.xml index fff0845dfb6c..cf74f8053349 100644 --- a/packages/CarSystemUI/res/values-te/strings.xml +++ b/packages/CarSystemUI/res/values-te/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ఏ యూజర్ అయినా మిగతా యూజర్ల కోసం యాప్లను అప్డేట్ చేయవచ్చు."</string> <string name="car_loading_profile" msgid="4507385037552574474">"లోడ్ అవుతోంది"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"యూజర్ను లోడ్ చేస్తోంది (<xliff:g id="FROM_USER">%1$d</xliff:g> నుండి <xliff:g id="TO_USER">%2$d</xliff:g> వరకు)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"మూసివేయి"</string> </resources> diff --git a/packages/CarSystemUI/res/values-ur/strings.xml b/packages/CarSystemUI/res/values-ur/strings.xml index ef65c7516956..abe9214181a2 100644 --- a/packages/CarSystemUI/res/values-ur/strings.xml +++ b/packages/CarSystemUI/res/values-ur/strings.xml @@ -28,6 +28,5 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"کوئی بھی صارف دیگر سبھی صارفین کے لیے ایپس کو اپ ڈیٹ کر سکتا ہے۔"</string> <string name="car_loading_profile" msgid="4507385037552574474">"لوڈ ہو رہی ہے"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"صارف کی نئی پروفائل لوڈ ہو رہی ہے (<xliff:g id="FROM_USER">%1$d</xliff:g> سے <xliff:g id="TO_USER">%2$d</xliff:g> کو)"</string> - <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> - <skip /> + <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"بند کریں"</string> </resources> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index 9aa7cc4ae29f..bdfbf82145c7 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -19,6 +19,8 @@ package com.android.companiondevicemanager; import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import static java.util.Objects.requireNonNull; + import android.app.Activity; import android.companion.CompanionDeviceManager; import android.content.Intent; @@ -129,6 +131,11 @@ public class DeviceChooserActivity extends Activity { } @Override + public String getCallingPackage() { + return requireNonNull(getService().mRequest.getCallingPackage()); + } + + @Override public void setTitle(CharSequence title) { final TextView titleView = findViewById(R.id.title); final int padding = getPadding(getResources()); diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index f7f3cbb7d332..12505bc55b15 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -422,7 +422,7 @@ public class DynamicSystemInstallationService extends Service private PendingIntent createPendingIntent(String action) { Intent intent = new Intent(this, DynamicSystemInstallationService.class); intent.setAction(action); - return PendingIntent.getService(this, 0, intent, 0); + return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); } private Notification buildNotification(int status, int cause) { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java index 2674eaf4b76c..0198168f9fda 100755 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java @@ -17,6 +17,7 @@ package com.android.packageinstaller; import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid; @@ -87,6 +88,8 @@ public class UninstallerActivity extends Activity { @Override public void onCreate(Bundle icicle) { + getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + // Never restore any state, esp. never create any fragments. The data in the fragment might // be stale, if e.g. the app was uninstalled while the activity was destroyed. super.onCreate(null); diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 32f6aa7774a7..6e4ce03c5d93 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -473,7 +473,7 @@ <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Najveći"</string> <string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Prilagodi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="content_description_menu_button" msgid="6254844309171779931">"Meni"</string> - <string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu demonstracije"</string> + <string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu rada za demonstraciju"</string> <string name="retail_demo_reset_next" msgid="3688129033843885362">"Naprijed"</string> <string name="retail_demo_reset_title" msgid="1866911701095959800">"Potrebna je lozinka"</string> <string name="active_input_method_subtypes" msgid="4232680535471633046">"Aktivne metode unosa"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index f6157b42312f..1d9994d52e5d 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -510,7 +510,7 @@ <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> - <string name="help_label" msgid="3528360748637781274">"Ayuda y sugerencias"</string> + <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string> <string name="storage_category" msgid="2287342585424631813">"Almacenamiento"</string> <string name="shared_data_title" msgid="1017034836800864953">"Datos compartidos"</string> <string name="shared_data_summary" msgid="5516326713822885652">"Ver y modificar los datos compartidos"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index b68aab46c028..8e13afd480f7 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Вибрати профіль"</string> <string name="category_personal" msgid="6236798763159385225">"Особисте"</string> - <string name="category_work" msgid="4014193632325996115">"Робота"</string> + <string name="category_work" msgid="4014193632325996115">"Робоче"</string> <string name="development_settings_title" msgid="140296922921597393">"Параметри розробника"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Увімкнути параметри розробника"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Установити параметри для розробки програми"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 2fd46d94d5cc..3cbf2685af26 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -15,6 +15,9 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.LocationManager; @@ -308,6 +311,36 @@ public class Utils { } /** + * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but + * preserves the alpha for a given drawable + * @param color + * @return a color matrix that uses the source alpha and given color + */ + public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) { + int r = Color.red(color); + int g = Color.green(color); + int b = Color.blue(color); + + ColorMatrix cm = new ColorMatrix(new float[] { + 0, 0, 0, 0, r, + 0, 0, 0, 0, g, + 0, 0, 0, 0, b, + 0, 0, 0, 1, 0 }); + + return cm; + } + + /** + * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics + * + * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on + * the source alpha + */ + public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) { + return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color)); + } + + /** * Determine whether a package is a "system package", in which case certain things (like * disabling notifications or disabling the package altogether) should be disallowed. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt index a5b5312707d0..5fa04f93e993 100644 --- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt +++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt @@ -108,6 +108,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) private val fillColorStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).also { p -> p.color = frameColor + p.alpha = 255 p.isDither = true p.strokeWidth = 5f p.style = Paint.Style.STROKE @@ -145,7 +146,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) // Only used if dualTone is set to true private val dualToneBackgroundFill = Paint(Paint.ANTI_ALIAS_FLAG).also { p -> p.color = frameColor - p.alpha = 255 + p.alpha = 85 // ~0.3 alpha by default p.isDither = true p.strokeWidth = 0f p.style = Paint.Style.FILL_AND_STROKE diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 1a2c2c8ac2e9..38ff447a71b5 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -52,6 +52,8 @@ class SettingsProtoDumpUtil { ConfigSettingsProto.ALARM_MANAGER_SETTINGS); namespaceToFieldMap.put(DeviceConfig.NAMESPACE_APP_COMPAT, ConfigSettingsProto.APP_COMPAT_SETTINGS); + namespaceToFieldMap.put(DeviceConfig.NAMESPACE_APP_STANDBY, + ConfigSettingsProto.APP_STANDBY_SETTINGS); namespaceToFieldMap.put(DeviceConfig.NAMESPACE_AUTOFILL, ConfigSettingsProto.AUTOFILL_SETTINGS); namespaceToFieldMap.put(DeviceConfig.NAMESPACE_BLOBSTORE, @@ -220,9 +222,6 @@ class SettingsProtoDumpUtil { final long appToken = p.start(GlobalSettingsProto.APP); dumpSetting(s, p, - Settings.Global.APP_IDLE_CONSTANTS, - GlobalSettingsProto.App.IDLE_CONSTANTS); - dumpSetting(s, p, Settings.Global.APP_STANDBY_ENABLED, GlobalSettingsProto.App.STANDBY_ENABLED); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 6dd2936cfb00..36213a020783 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3342,7 +3342,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 193; + private static final int SETTINGS_VERSION = 194; private final int mUserId; @@ -4754,6 +4754,13 @@ public class SettingsProvider extends ContentProvider { currentVersion = 193; } + if (currentVersion == 193) { + // Version 193: remove obsolete LOCATION_PROVIDERS_ALLOWED settings + getSecureSettingsLocked(userId).deleteSettingLocked( + Secure.LOCATION_PROVIDERS_ALLOWED); + currentVersion = 194; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index de4325098a7f..baa266a6e5cd 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -127,7 +127,6 @@ public class SettingsBackupTest { Settings.Global.APN_DB_UPDATE_CONTENT_URL, Settings.Global.APN_DB_UPDATE_METADATA_URL, Settings.Global.APP_BINDING_CONSTANTS, - Settings.Global.APP_IDLE_CONSTANTS, Settings.Global.APP_OPS_CONSTANTS, Settings.Global.APP_STANDBY_ENABLED, Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE, @@ -443,6 +442,7 @@ public class SettingsBackupTest { Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, Settings.Global.SHOW_PEOPLE_SPACE, Settings.Global.SHOW_NEW_LOCKSCREEN, + Settings.Global.SHOW_NEW_NOTIF_DISMISS, Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, Settings.Global.SHOW_TEMPERATURE_WARNING, Settings.Global.SHOW_USB_TEMPERATURE_ALARM, @@ -745,7 +745,6 @@ public class SettingsBackupTest { Settings.Secure.FACE_UNLOCK_RE_ENROLL, Settings.Secure.TAP_GESTURE, Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable - Settings.Secure.WINDOW_MAGNIFICATION, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER, Settings.Secure.SUPPRESS_DOZE, Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 02751e27874d..5200d95d8d58 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -792,7 +792,7 @@ public class BugreportProgressService extends Service { intent.setClass(context, BugreportProgressService.class); intent.putExtra(EXTRA_ID, info.id); return PendingIntent.getService(context, info.id, intent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } /** @@ -1252,7 +1252,7 @@ public class BugreportProgressService extends Service { .setTicker(title) .setContentText(content) .setContentIntent(PendingIntent.getService(mContext, info.id, shareIntent, - PendingIntent.FLAG_UPDATE_CURRENT)) + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .setOnlyAlertOnce(false) .setDeleteIntent(newCancelIntent(mContext, info)); diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index 31a33fbfc308..ccd235d54c0b 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -5,6 +5,4 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorRadius="130px" - systemui:sensorCenterY="1636px" systemui:sensorTouchAreaCoefficient="0.5"/> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index a62502965dd2..78d92c4b47e2 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -158,8 +158,6 @@ </declare-styleable> <declare-styleable name="UdfpsView"> - <attr name="sensorRadius" format="dimension"/> - <attr name="sensorCenterY" format="dimension"/> <attr name="sensorPressureCoefficient" format="float"/> <attr name="sensorTouchAreaCoefficient" format="float"/> </declare-styleable> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index f2bb4907ee8f..7d3135ac1937 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -155,6 +155,7 @@ <item type="id" name="scale_y_dynamicanimation_tag"/> <item type="id" name="physics_animator_tag"/> <item type="id" name="target_animator_tag" /> + <item type="id" name="reorder_animator_tag"/> <!-- Global Actions Menu --> <item type="id" name="global_actions_view" /> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 1a98c206eb0d..9beb7db40096 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2102,7 +2102,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (isEncryptedOrLockdown(userId)) { mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback, - userId, null /* surface */); + userId); } else { mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal, mFingerprintAuthenticationCallback, null /* handler */, userId); diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 8ac6edb9dfa2..cf576dd6b964 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -37,7 +37,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -308,7 +307,6 @@ public class Dependency { @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil; @Inject Lazy<SmartReplyController> mSmartReplyController; @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler; - @Inject Lazy<Bubbles> mBubbles; @Inject Lazy<NotificationEntryManager> mNotificationEntryManager; @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager; @Inject Lazy<AutoHideController> mAutoHideController; @@ -506,7 +504,6 @@ public class Dependency { mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, mRemoteInputQuickSettingsDisabler::get); - mProviders.put(Bubbles.class, mBubbles::get); mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get); mProviders.put(ForegroundServiceNotificationListener.class, mForegroundServiceNotificationListener::get); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index e709830342b5..bf42a60ac033 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -34,6 +34,7 @@ import android.provider.Settings; import android.util.Log; import android.util.TimingsTraceLog; +import com.android.internal.protolog.common.ProtoLog; import com.android.systemui.dagger.ContextComponentHelper; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; @@ -69,6 +70,8 @@ public class SystemUIApplication extends Application implements public SystemUIApplication() { super(); Log.v(TAG, "SystemUIApplication constructed."); + // SysUI may be building without protolog preprocessing in some cases + ProtoLog.REQUIRE_PROTOLOGTOOL = false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 187c31f43a64..4f9d84de36a2 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -101,11 +101,13 @@ public class SystemUIFactory { if (initializeComponents) { // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment - builder = builder.setPip(mWMComponent.getPip()) + builder = prepareSysUIComponentBuilder(builder, mWMComponent) + .setPip(mWMComponent.getPip()) .setSplitScreen(mWMComponent.getSplitScreen()) .setOneHanded(mWMComponent.getOneHanded()) .setShellDump(mWMComponent.getShellDump()); } else { + // TODO: Call on prepareSysUIComponentBuilder but not with real components. builder = builder.setPip(Optional.ofNullable(null)) .setSplitScreen(Optional.ofNullable(null)) .setOneHanded(Optional.ofNullable(null)) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index e3b00495f3dc..3c2e00869ab8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -78,7 +78,7 @@ class UdfpsController implements DozeReceiver { // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting - final int mUdfpsSensorId; + final FingerprintSensorPropertiesInternal mSensorProps; private final WindowManager mWindowManager; private final SystemSettings mSystemSettings; private final DelayableExecutor mFgExecutor; @@ -180,19 +180,12 @@ class UdfpsController implements DozeReceiver { mFgExecutor = fgExecutor; mLayoutParams = createLayoutParams(context); - int udfpsSensorId = -1; - for (FingerprintSensorPropertiesInternal props : - mFingerprintManager.getSensorPropertiesInternal()) { - if (props.isAnyUdfpsType()) { - udfpsSensorId = props.sensorId; - break; - } - } + mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists - checkArgument(udfpsSensorId != -1); - mUdfpsSensorId = udfpsSensorId; + checkArgument(mSensorProps != null); mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false); + mView.setSensorProperties(mSensorProps); mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path); mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command); @@ -235,6 +228,17 @@ class UdfpsController implements DozeReceiver { mIsOverlayShowing = false; } + @Nullable + private FingerprintSensorPropertiesInternal findFirstUdfps() { + for (FingerprintSensorPropertiesInternal props : + mFingerprintManager.getSensorPropertiesInternal()) { + if (props.isAnyUdfpsType()) { + return props; + } + } + return null; + } + @Override public void dozeTimeTick() { mView.dozeTimeTick(); @@ -374,7 +378,7 @@ class UdfpsController implements DozeReceiver { fw.write(mHbmEnableCommand); fw.close(); } - mFingerprintManager.onPointerDown(mUdfpsSensorId, x, y, minor, major); + mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); } catch (IOException e) { mView.hideScrimAndDot(); Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage()); @@ -382,7 +386,7 @@ class UdfpsController implements DozeReceiver { } private void onFingerUp() { - mFingerprintManager.onPointerUp(mUdfpsSensorId); + mFingerprintManager.onPointerUp(mSensorProps.sensorId); // Hiding the scrim before disabling HBM results in less noticeable flicker. mView.hideScrimAndDot(); if (mHbmSupported) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index d7e91384f049..0ed3bda40c4b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -25,6 +26,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -55,8 +57,6 @@ public class UdfpsView extends View implements DozeReceiver, private final RectF mSensorRect; private final Paint mSensorPaint; - private final float mSensorRadius; - private final float mSensorCenterY; private final float mSensorTouchAreaCoefficient; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; @@ -64,9 +64,7 @@ public class UdfpsView extends View implements DozeReceiver, private final Rect mTouchableRegion; private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener; - // This is calculated from the screen's dimensions at runtime, as opposed to mSensorCenterY, - // which is defined in layout.xml - private float mSensorCenterX; + @NonNull private FingerprintSensorPropertiesInternal mProps; // AOD anti-burn-in offsets private float mInterpolatedDarkAmount; @@ -83,18 +81,10 @@ public class UdfpsView extends View implements DozeReceiver, TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0, 0); try { - if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) { - throw new IllegalArgumentException("UdfpsView must contain sensorRadius"); - } - if (!a.hasValue(R.styleable.UdfpsView_sensorCenterY)) { - throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom"); - } if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) { throw new IllegalArgumentException( "UdfpsView must contain sensorTouchAreaCoefficient"); } - mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f); - mSensorCenterY = a.getDimension(R.styleable.UdfpsView_sensorCenterY, 0f); mSensorTouchAreaCoefficient = a.getFloat( R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f); } finally { @@ -134,6 +124,10 @@ public class UdfpsView extends View implements DozeReceiver, mIsScrimShowing = false; } + void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) { + mProps = properties; + } + @Override public void dozeTimeTick() { updateAodPosition(); @@ -165,9 +159,10 @@ public class UdfpsView extends View implements DozeReceiver, final int h = getLayoutParams().height; final int w = getLayoutParams().width; mScrimRect.set(0 /* left */, 0 /* top */, w, h); - mSensorCenterX = w / 2f; - mSensorRect.set(mSensorCenterX - mSensorRadius, mSensorCenterY - mSensorRadius, - mSensorCenterX + mSensorRadius, mSensorCenterY + mSensorRadius); + mSensorRect.set(mProps.sensorLocationX - mProps.sensorRadius, + mProps.sensorLocationY - mProps.sensorRadius, + mProps.sensorLocationX + mProps.sensorRadius, + mProps.sensorLocationY + mProps.sensorRadius); // Sets mTouchableRegion with rounded up values from mSensorRect. mSensorRect.roundOut(mTouchableRegion); @@ -211,10 +206,10 @@ public class UdfpsView extends View implements DozeReceiver, } boolean isValidTouch(float x, float y, float pressure) { - return x > (mSensorCenterX - mSensorRadius * mSensorTouchAreaCoefficient) - && x < (mSensorCenterX + mSensorRadius * mSensorTouchAreaCoefficient) - && y > (mSensorCenterY - mSensorRadius * mSensorTouchAreaCoefficient) - && y < (mSensorCenterY + mSensorRadius * mSensorTouchAreaCoefficient); + return x > (mProps.sensorLocationX - mProps.sensorRadius * mSensorTouchAreaCoefficient) + && x < (mProps.sensorLocationX + mProps.sensorRadius * mSensorTouchAreaCoefficient) + && y > (mProps.sensorLocationY - mProps.sensorRadius * mSensorTouchAreaCoefficient) + && y < (mProps.sensorLocationY + mProps.sensorRadius * mSensorTouchAreaCoefficient); } void setScrimAlpha(int alpha) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 1891daf59007..09d9e03cb0cb 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -53,7 +53,8 @@ import java.util.Objects; /** * Encapsulates the data and UI elements of a bubble. */ -class Bubble implements BubbleViewProvider { +@VisibleForTesting +public class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; private final String mKey; @@ -62,7 +63,7 @@ class Bubble implements BubbleViewProvider { private long mLastAccessed; @Nullable - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; /** Whether the bubble should show a dot for the notification indicating updated content. */ private boolean mShowBubbleUpdateDot = true; @@ -173,8 +174,8 @@ class Bubble implements BubbleViewProvider { @VisibleForTesting(visibility = PRIVATE) Bubble(@NonNull final BubbleEntry entry, - @Nullable final BubbleController.NotificationSuppressionChangedListener listener, - final BubbleController.PendingIntentCanceledListener intentCancelListener) { + @Nullable final Bubbles.NotificationSuppressionChangedListener listener, + final Bubbles.PendingIntentCanceledListener intentCancelListener) { mKey = entry.getKey(); mSuppressionListener = listener; mIntentCancelListener = intent -> { @@ -309,11 +310,13 @@ class Bubble implements BubbleViewProvider { * * @param callback the callback to notify one the bubble is ready to be displayed. * @param context the context for the bubble. + * @param controller * @param stackView the stackView the bubble is eventually added to. * @param iconFactory the iconfactory use to create badged images for the bubble. */ void inflate(BubbleViewInfoTask.Callback callback, Context context, + BubbleController controller, BubbleStackView stackView, BubbleIconFactory iconFactory, boolean skipInflation) { @@ -322,6 +325,7 @@ class Bubble implements BubbleViewProvider { } mInflationTask = new BubbleViewInfoTask(this, context, + controller, stackView, iconFactory, skipInflation, @@ -403,7 +407,7 @@ class Bubble implements BubbleViewProvider { mInstanceId = entry.getStatusBarNotification().getInstanceId(); mFlyoutMessage = extractFlyoutMessage(entry); if (entry.getRanking() != null) { - mShortcutInfo = entry.getRanking().getShortcutInfo(); + mShortcutInfo = entry.getRanking().getConversationShortcutInfo(); mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive(); if (entry.getRanking().getChannel() != null) { mIsImportantConversation = @@ -522,7 +526,8 @@ class Bubble implements BubbleViewProvider { /** * Sets whether this notification should be suppressed in the shade. */ - void setSuppressNotification(boolean suppressNotification) { + @VisibleForTesting + public void setSuppressNotification(boolean suppressNotification) { boolean prevShowInShade = showInShade(); if (suppressNotification) { mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; @@ -559,7 +564,8 @@ class Bubble implements BubbleViewProvider { /** * Whether the flyout for the bubble should be shown. */ - boolean showFlyout() { + @VisibleForTesting + public boolean showFlyout() { return !mSuppressFlyout && !mShouldSuppressPeek && !shouldSuppressNotification() && !mShouldSuppressNotificationList; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 0c3dc8222a34..598a604099ac 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -17,39 +17,19 @@ package com.android.systemui.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.Notification.FLAG_BUBBLE; -import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; -import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; -import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; -import static android.service.notification.NotificationListenerService.REASON_CLICK; -import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; -import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; -import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.systemui.statusbar.StatusBarState.SHADE; -import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.NonNull; import android.annotation.UserIdInt; -import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.INotificationManager; import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ActivityInfo; @@ -65,7 +45,6 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; -import android.service.notification.ZenModeConfig; import android.util.ArraySet; import android.util.Log; import android.util.Pair; @@ -74,45 +53,14 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import androidx.annotation.IntDef; import androidx.annotation.MainThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.Dumpable; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.model.SysUiState; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.TaskStackChangeListener; -import com.android.systemui.shared.system.TaskStackChangeListeners; -import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationRemoveInterceptor; -import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.notification.NotificationChannelHelper; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifCollection; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -120,11 +68,10 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.IntConsumer; /** * Bubbles are a special type of content that can "float" on top of other apps or System UI. @@ -132,52 +79,23 @@ import java.util.Objects; * * The controller manages addition, removal, and visible state of bubbles on screen. */ -public class BubbleController implements Bubbles, ConfigurationController.ConfigurationListener, - Dumpable { +public class BubbleController implements Bubbles { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES; - @Retention(SOURCE) - @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, - DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, - DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, - DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, - DISMISS_NO_BUBBLE_UP}) - @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) - @interface DismissReason {} - - static final int DISMISS_USER_GESTURE = 1; - static final int DISMISS_AGED = 2; - static final int DISMISS_TASK_FINISHED = 3; - static final int DISMISS_BLOCKED = 4; - static final int DISMISS_NOTIF_CANCEL = 5; - static final int DISMISS_ACCESSIBILITY_ACTION = 6; - static final int DISMISS_NO_LONGER_BUBBLE = 7; - static final int DISMISS_USER_CHANGED = 8; - static final int DISMISS_GROUP_CANCELLED = 9; - static final int DISMISS_INVALID_INTENT = 10; - static final int DISMISS_OVERFLOW_MAX_REACHED = 11; - static final int DISMISS_SHORTCUT_REMOVED = 12; - static final int DISMISS_PACKAGE_REMOVED = 13; - static final int DISMISS_NO_BUBBLE_UP = 14; - private final Context mContext; - private final NotificationEntryManager mNotificationEntryManager; - private final NotifPipeline mNotifPipeline; - private final BubbleTaskStackListener mTaskStackListener; private BubbleExpandListener mExpandListener; @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer; - private final NotificationGroupManagerLegacy mNotificationGroupManager; - private final ShadeController mShadeController; private final FloatingContentCoordinator mFloatingContentCoordinator; private final BubbleDataRepository mDataRepository; private BubbleLogger mLogger; private final Handler mMainHandler; private BubbleData mBubbleData; - private ScrimView mBubbleScrim; + private View mBubbleScrim; @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; private BubblePositioner mBubblePositioner; + private SysuiProxy mSysuiProxy; /** * The relative position of the stack when we removed it and nulled it out. If the stack is @@ -193,12 +111,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config // Used when ranking updates occur and we check if things should bubble / unbubble private NotificationListenerService.Ranking mTmpRanking; - // Bubbles get added to the status bar view - private final NotificationShadeWindowController mNotificationShadeWindowController; - private final ZenModeController mZenModeController; - private StatusBarStateListener mStatusBarStateListener; - private INotificationManager mINotificationManager; - // Callback that updates BubbleOverflowActivity on data change. @Nullable private BubbleData.Listener mOverflowListener = null; @@ -209,12 +121,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * When the shade status changes to SHADE (from anything but SHADE, like LOCKED) we'll select * this bubble and expand the stack. */ - @Nullable private NotificationEntry mNotifEntryToExpandOnShadeUnlock; + @Nullable private BubbleEntry mNotifEntryToExpandOnShadeUnlock; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private IStatusBarService mBarService; private WindowManager mWindowManager; - private SysUiState mSysUiState; // Used to post to main UI thread private Handler mHandler = new Handler(); @@ -224,9 +134,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** Whether or not the BubbleStackView has been added to the WindowManager. */ private boolean mAddedToWindowManager = false; - // Listens to user switch so bubbles can be saved and restored. - private final NotificationLockscreenUserManager mNotifUserManager; - /** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */ private int mOrientation = Configuration.ORIENTATION_UNDEFINED; @@ -247,9 +154,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private ShellTaskOrganizer mTaskOrganizer; - // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline - private final List<NotifCallback> mCallbacks = new ArrayList<>(); - /** * Whether the IME is visible, as reported by the BubbleStackView. If it is, we'll make the * Bubbles window NOT_FOCUSABLE so that touches on the Bubbles UI doesn't steal focus from the @@ -257,122 +161,15 @@ public class BubbleController implements Bubbles, ConfigurationController.Config */ private boolean mImeVisible = false; - /** - * Listener to find out about stack expansion / collapse events. - */ - public interface BubbleExpandListener { - /** - * Called when the expansion state of the bubble stack changes. - * - * @param isExpanding whether it's expanding or collapsing - * @param key the notification key associated with bubble being expanded - */ - void onBubbleExpandChanged(boolean isExpanding, String key); - } - - /** - * Listener to be notified when a bubbles' notification suppression state changes. - */ - public interface NotificationSuppressionChangedListener { - /** - * Called when the notification suppression state of a bubble changes. - */ - void onBubbleNotificationSuppressionChange(Bubble bubble); - } - - /** - * Listener to be notified when a pending intent has been canceled for a bubble. - */ - public interface PendingIntentCanceledListener { - /** - * Called when the pending intent for a bubble has been canceled. - */ - void onPendingIntentCanceled(Bubble bubble); - } - - /** - * Callback for when the BubbleController wants to interact with the notification pipeline to: - * - Remove a previously bubbled notification - * - Update the notification shade since bubbled notification should/shouldn't be showing - */ - public interface NotifCallback { - /** - * Called when a bubbled notification that was hidden from the shade is now being removed - * This can happen when an app cancels a bubbled notification or when the user dismisses a - * bubble. - */ - void removeNotification( - @NonNull NotificationEntry entry, - @NonNull DismissedByUserStats stats, - int reason); - - /** - * Called when a bubbled notification has changed whether it should be - * filtered from the shade. - */ - void invalidateNotifications(@NonNull String reason); - - /** - * Called on a bubbled entry that has been removed when there are no longer - * bubbled entries in its group. - * - * Checks whether its group has any other (non-bubbled) children. If it doesn't, - * removes all remnants of the group's summary from the notification pipeline. - * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. - */ - void maybeCancelSummary(@NonNull NotificationEntry entry); - } - - /** - * Listens for the current state of the status bar and updates the visibility state - * of bubbles as needed. - */ - private class StatusBarStateListener implements StatusBarStateController.StateListener { - private int mState; - /** - * Returns the current status bar state. - */ - public int getCurrentState() { - return mState; - } - - @Override - public void onStateChanged(int newState) { - mState = newState; - boolean shouldCollapse = (mState != SHADE); - if (shouldCollapse) { - collapseStack(); - } - - if (mNotifEntryToExpandOnShadeUnlock != null) { - expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock); - mNotifEntryToExpandOnShadeUnlock = null; - } - - updateStack(); - } - } + /** true when user is in status bar unlock shade. */ + private boolean mIsStatusBarShade = true; /** * Injected constructor. See {@link BubbleModule}. */ public static BubbleController create(Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, FloatingContentCoordinator floatingContentCoordinator, - SysUiState sysUiState, - INotificationManager notificationManager, @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, @@ -381,39 +178,23 @@ public class BubbleController implements Bubbles, ConfigurationController.Config @Main Handler mainHandler, ShellTaskOrganizer organizer) { BubbleLogger logger = new BubbleLogger(uiEventLogger); - return new BubbleController(context, notificationShadeWindowController, - statusBarStateController, shadeController, new BubbleData(context, logger), - synchronizer, configurationController, interruptionStateProvider, zenModeController, - notifUserManager, groupManager, entryManager, notifPipeline, featureFlags, - dumpManager, floatingContentCoordinator, - new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager, - statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger, - mainHandler, organizer, new BubblePositioner(context, windowManager)); + return new BubbleController(context, + new BubbleData(context, logger), synchronizer, + floatingContentCoordinator, new BubbleDataRepository(context, launcherApps), + statusBarService, windowManager, + windowManagerShellWrapper, launcherApps, logger, mainHandler, organizer, + new BubblePositioner(context, windowManager)); } /** * Testing constructor. */ @VisibleForTesting - BubbleController(Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, + public BubbleController(Context context, BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, FloatingContentCoordinator floatingContentCoordinator, BubbleDataRepository dataRepository, - SysUiState sysUiState, - INotificationManager notificationManager, @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, @@ -422,82 +203,35 @@ public class BubbleController implements Bubbles, ConfigurationController.Config Handler mainHandler, ShellTaskOrganizer organizer, BubblePositioner positioner) { - dumpManager.registerDumpable(TAG, this); mContext = context; - mShadeController = shadeController; - mNotificationInterruptStateProvider = interruptionStateProvider; - mNotifUserManager = notifUserManager; - mZenModeController = zenModeController; mFloatingContentCoordinator = floatingContentCoordinator; mDataRepository = dataRepository; - mINotificationManager = notificationManager; mLogger = bubbleLogger; mMainHandler = mainHandler; - mZenModeController.addCallback(new ZenModeController.Callback() { - @Override - public void onZenChanged(int zen) { - for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade()); - } - } - - @Override - public void onConfigChanged(ZenModeConfig config) { - for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade()); - } - } - }); - - configurationController.addCallback(this /* configurationListener */); - mSysUiState = sysUiState; mBubbleData = data; mBubbleData.setListener(mBubbleDataListener); - mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() { - @Override - public void onBubbleNotificationSuppressionChange(Bubble bubble) { - // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it - // can tell. - try { - mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), - !bubble.showInShade()); - } catch (RemoteException e) { - // Bad things have happened - } + mBubbleData.setSuppressionChangedListener(bubble -> { + // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it + // can tell. + try { + mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), + !bubble.showInShade()); + } catch (RemoteException e) { + // Bad things have happened } }); mBubbleData.setPendingIntentCancelledListener(bubble -> { if (bubble.getBubbleIntent() == null) { return; } - if (bubble.isIntentActive() - || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { + if (bubble.isIntentActive() || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { bubble.setPendingIntentCanceled(); return; } - mHandler.post( - () -> removeBubble(bubble.getKey(), - BubbleController.DISMISS_INVALID_INTENT)); + mHandler.post(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT)); }); - mNotificationEntryManager = entryManager; - mNotificationGroupManager = groupManager; - mNotifPipeline = notifPipeline; - - if (!featureFlags.isNewNotifPipelineRenderingEnabled()) { - setupNEM(); - } else { - setupNotifPipeline(); - } - - mNotificationShadeWindowController = notificationShadeWindowController; - mStatusBarStateListener = new StatusBarStateListener(); - statusBarStateController.addCallback(mStatusBarStateListener); - - mTaskStackListener = new BubbleTaskStackListener(); - TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); - try { windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener()); } catch (RemoteException e) { @@ -511,25 +245,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config ServiceManager.getService(Context.STATUS_BAR_SERVICE)) : statusBarService; - mBubbleScrim = new ScrimView(mContext); - mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - mSavedBubbleKeysPerUser = new SparseSetArray<>(); - mCurrentUserId = mNotifUserManager.getCurrentUserId(); + mCurrentUserId = ActivityManager.getCurrentUser(); mBubbleData.setCurrentUserId(mCurrentUserId); - mNotifUserManager.addUserChangedListener( - new NotificationLockscreenUserManager.UserChangedListener() { - @Override - public void onUserChanged(int newUserId) { - BubbleController.this.saveBubbles(mCurrentUserId); - mBubbleData.dismissAll(DISMISS_USER_CHANGED); - BubbleController.this.restoreBubbles(newUserId); - mCurrentUserId = newUserId; - mBubbleData.setCurrentUserId(newUserId); - } - }); - mBubbleIconFactory = new BubbleIconFactory(context); mTaskOrganizer = organizer; mBubblePositioner = positioner; @@ -549,10 +268,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public void onPackagesAvailable(String[] strings, UserHandle userHandle, - boolean b) { - - } + public void onPackagesAvailable(String[] strings, UserHandle userHandle, boolean b) {} @Override public void onPackagesUnavailable(String[] packages, UserHandle userHandle, @@ -577,17 +293,9 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } /** - * See {@link NotifCallback}. - */ - @Override - public void addNotifCallback(NotifCallback callback) { - mCallbacks.add(callback); - } - - /** * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal. */ - public void hideCurrentInputMethod() { + void hideCurrentInputMethod() { try { mBarService.hideCurrentInputMethodForBubbles(); } catch (RemoteException e) { @@ -595,183 +303,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - private void onBubbleExpandChanged(boolean shouldExpand) { - mSysUiState - .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand) - .commitUpdate(mContext.getDisplayId()); - } - - private void setupNEM() { - mNotificationEntryManager.addNotificationEntryListener( - new NotificationEntryListener() { - @Override - public void onPendingEntryAdded(NotificationEntry entry) { - onEntryAdded(entry); - } - - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - onEntryUpdated(entry); - } - - @Override - public void onEntryRemoved( - NotificationEntry entry, - @android.annotation.Nullable NotificationVisibility visibility, - boolean removedByUser, - int reason) { - BubbleController.this.onEntryRemoved(entry); - } - - @Override - public void onNotificationRankingUpdated(RankingMap rankingMap) { - onRankingUpdated(rankingMap); - } - }); - - // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator - mNotificationEntryManager.addNotificationRemoveInterceptor( - new NotificationRemoveInterceptor() { - @Override - public boolean onNotificationRemoveRequested( - String key, - NotificationEntry entry, - int dismissReason) { - final boolean isClearAll = dismissReason == REASON_CANCEL_ALL; - final boolean isUserDismiss = dismissReason == REASON_CANCEL - || dismissReason == REASON_CLICK; - final boolean isAppCancel = dismissReason == REASON_APP_CANCEL - || dismissReason == REASON_APP_CANCEL_ALL; - final boolean isSummaryCancel = - dismissReason == REASON_GROUP_SUMMARY_CANCELED; - - // Need to check for !appCancel here because the notification may have - // previously been dismissed & entry.isRowDismissed would still be true - boolean userRemovedNotif = - (entry != null && entry.isRowDismissed() && !isAppCancel) - || isClearAll || isUserDismiss || isSummaryCancel; - - if (userRemovedNotif) { - return handleDismissalInterception(entry); - } - return false; - } - }); - - mNotificationGroupManager.registerGroupChangeListener( - new NotificationGroupManagerLegacy.OnGroupChangeListener() { - @Override - public void onGroupSuppressionChanged( - NotificationGroupManagerLegacy.NotificationGroup group, - boolean suppressed) { - // More notifications could be added causing summary to no longer - // be suppressed -- in this case need to remove the key. - final String groupKey = group.summary != null - ? group.summary.getSbn().getGroupKey() - : null; - if (!suppressed && groupKey != null - && mBubbleData.isSummarySuppressed(groupKey)) { - mBubbleData.removeSuppressedSummary(groupKey); - } - } - }); - - addNotifCallback(new NotifCallback() { - @Override - public void removeNotification( - NotificationEntry entry, - DismissedByUserStats dismissedByUserStats, - int reason - ) { - mNotificationEntryManager.performRemoveNotification(entry.getSbn(), - dismissedByUserStats, reason); - } - - @Override - public void invalidateNotifications(String reason) { - mNotificationEntryManager.updateNotifications(reason); - } - - @Override - public void maybeCancelSummary(NotificationEntry entry) { - // Check if removed bubble has an associated suppressed group summary that needs - // to be removed now. - final String groupKey = entry.getSbn().getGroupKey(); - if (mBubbleData.isSummarySuppressed(groupKey)) { - mBubbleData.removeSuppressedSummary(groupKey); - - final NotificationEntry summary = - mNotificationEntryManager.getActiveNotificationUnfiltered( - mBubbleData.getSummaryKey(groupKey)); - if (summary != null) { - mNotificationEntryManager.performRemoveNotification( - summary.getSbn(), - getDismissedByUserStats(summary, false), - UNDEFINED_DISMISS_REASON); - } - } - - // Check if we still need to remove the summary from NoManGroup because the summary - // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above. - // For example: - // 1. Bubbled notifications (group) is posted to shade and are visible bubbles - // 2. User expands bubbles so now their respective notifications in the shade are - // hidden, including the group summary - // 3. User removes all bubbles - // 4. We expect all the removed bubbles AND the summary (note: the summary was - // never added to the suppressedSummary list in BubbleData, so we add this check) - NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry); - if (summary != null) { - ArrayList<NotificationEntry> summaryChildren = - mNotificationGroupManager.getLogicalChildren(summary.getSbn()); - boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey()); - if (!isSummaryThisNotif && (summaryChildren == null - || summaryChildren.isEmpty())) { - mNotificationEntryManager.performRemoveNotification( - summary.getSbn(), - getDismissedByUserStats(summary, false), - UNDEFINED_DISMISS_REASON); - } - } - } - }); - } - - private void setupNotifPipeline() { - mNotifPipeline.addCollectionListener(new NotifCollectionListener() { - @Override - public void onEntryAdded(NotificationEntry entry) { - BubbleController.this.onEntryAdded(entry); - } - - @Override - public void onEntryUpdated(NotificationEntry entry) { - BubbleController.this.onEntryUpdated(entry); - } - - @Override - public void onRankingUpdate(RankingMap rankingMap) { - onRankingUpdated(rankingMap); - } - - @Override - public void onEntryRemoved(NotificationEntry entry, - @NotifCollection.CancellationReason int reason) { - BubbleController.this.onEntryRemoved(entry); - } - }); - } - - /** - * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController} - * since we want the scrim's appearance and behavior to be identical to that of the notification - * shade scrim. - */ - @Override - public ScrimView getScrimForBubble() { - return mBubbleScrim; - } - /** * Called when the status bar has become visible or invisible (either permanently or * temporarily). @@ -785,16 +316,47 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } + @Override + public void onZenStateChanged() { + for (Bubble b : mBubbleData.getBubbles()) { + b.setShowDot(b.showInShade()); + } + } + + @Override + public void onStatusBarStateChanged(boolean isShade) { + mIsStatusBarShade = isShade; + if (!mIsStatusBarShade) { + collapseStack(); + } + + if (mNotifEntryToExpandOnShadeUnlock != null) { + expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock); + mNotifEntryToExpandOnShadeUnlock = null; + } + + updateStack(); + } + + @Override + public void onUserChanged(int newUserId) { + saveBubbles(mCurrentUserId); + mBubbleData.dismissAll(DISMISS_USER_CHANGED); + restoreBubbles(newUserId); + mCurrentUserId = newUserId; + mBubbleData.setCurrentUserId(newUserId); + } + /** * Sets whether to perform inflation on the same thread as the caller. This method should only * be used in tests, not in production. */ @VisibleForTesting - void setInflateSynchronously(boolean inflateSynchronously) { + public void setInflateSynchronously(boolean inflateSynchronously) { mInflateSynchronously = inflateSynchronously; } - @Override + /** Set a listener to be notified of when overflow view update. */ public void setOverflowListener(BubbleData.Listener listener) { mOverflowListener = listener; } @@ -802,21 +364,24 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** * @return Bubbles for updating overflow. */ - @Override - public List<Bubble> getOverflowBubbles() { + List<Bubble> getOverflowBubbles() { return mBubbleData.getOverflowBubbles(); } - @Override + /** The task listener for events in bubble tasks. */ public ShellTaskOrganizer getTaskOrganizer() { return mTaskOrganizer; } - @Override - public BubblePositioner getPositioner() { + /** Contains information to help position things on the screen. */ + BubblePositioner getPositioner() { return mBubblePositioner; } + SysuiProxy getSysuiProxy() { + return mSysuiProxy; + } + /** * BubbleStackView is lazily created by this method the first time a Bubble is added. This * method initializes the stack view and adds it to the StatusBar just above the scrim. @@ -824,23 +389,14 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private void ensureStackViewCreated() { if (mStackView == null) { mStackView = new BubbleStackView( - mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, - this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged, - this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner); + mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator); mStackView.setStackStartPosition(mPositionFromRemovedStack); mStackView.addView(mBubbleScrim); mStackView.onOrientationChanged(); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } - - mStackView.setUnbubbleConversationCallback(key -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); - if (entry != null) { - onUserChangedBubble(entry, false /* shouldBubble */); - } - }); + mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation); } addToWindowManagerMaybe(); @@ -882,7 +438,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - private void onImeVisibilityChanged(boolean imeVisible) { + void onImeVisibilityChanged(boolean imeVisible) { mImeVisible = imeVisible; } @@ -913,7 +469,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been * added in the meantime. */ - private void onAllBubblesAnimatedOut() { + void onAllBubblesAnimatedOut() { if (mStackView != null) { mStackView.setVisibility(INVISIBLE); removeFromWindowManagerMaybe(); @@ -946,12 +502,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config // There were no bubbles saved for this used. return; } - for (NotificationEntry e : - mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { - if (savedBubbleKeys.contains(e.getKey()) - && mNotificationInterruptStateProvider.shouldBubbleUp(e) - && e.isBubble() - && canLaunchInActivityView(mContext, e)) { + for (BubbleEntry e : mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys)) { + if (canLaunchInActivityView(mContext, e)) { updateBubble(e, true /* suppressFlyout */, false /* showInShade */); } } @@ -960,27 +512,18 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public void onUiModeChanged() { - updateForThemeChanges(); - } - - @Override - public void onOverlayChanged() { - updateForThemeChanges(); - } - - private void updateForThemeChanges() { + public void updateForThemeChanges() { if (mStackView != null) { mStackView.onThemeChanged(); } mBubbleIconFactory = new BubbleIconFactory(mContext); // Reload each bubble for (Bubble b: mBubbleData.getBubbles()) { - b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, + b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); } for (Bubble b: mBubbleData.getOverflowBubbles()) { - b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, + b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); } } @@ -1012,9 +555,16 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - /** - * Set a listener to be notified of bubble expand events. - */ + @Override + public void setBubbleScrim(View view) { + mBubbleScrim = view; + } + + @Override + public void setSysuiProxy(SysuiProxy proxy) { + mSysuiProxy = proxy; + } + @Override public void setExpandListener(BubbleExpandListener listener) { mExpandListener = ((isExpanding, key) -> { @@ -1032,7 +582,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * screen (e.g. if on AOD). */ @VisibleForTesting - boolean hasBubbles() { + public boolean hasBubbles() { if (mStackView == null) { return false; } @@ -1050,24 +600,37 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) { - String key = entry.getKey(); + public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) { boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key) && !mBubbleData.getAnyBubbleWithkey(key).showInShade()); - String groupKey = entry.getSbn().getGroupKey(); boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey); boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey)); return (isSummary && isSuppressedSummary) || isSuppressedBubble; } @Override - public boolean isBubbleExpanded(NotificationEntry entry) { - return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null - && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey()); + public boolean isSummarySuppressed(String groupKey) { + return mBubbleData.isSummarySuppressed(groupKey); } @Override + public void removeSuppressedSummary(String groupKey) { + mBubbleData.removeSuppressedSummary(groupKey); + } + + @Override + public String getSummaryKey(String groupKey) { + return mBubbleData.getSummaryKey(groupKey); + } + + @Override + public boolean isBubbleExpanded(String key) { + return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null + && mBubbleData.getSelectedBubble().getKey().equals(key); + } + + /** Promote the provided bubble from the overflow view. */ public void promoteBubbleFromOverflow(Bubble bubble) { mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK); bubble.setInflateSynchronously(mInflateSynchronously); @@ -1077,8 +640,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public void expandStackAndSelectBubble(NotificationEntry entry) { - if (mStatusBarStateListener.getCurrentState() == SHADE) { + public void expandStackAndSelectBubble(BubbleEntry entry) { + if (mIsStatusBarShade) { mNotifEntryToExpandOnShadeUnlock = null; String key = entry.getKey(); @@ -1104,27 +667,13 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - @Override - public void onUserChangedImportance(NotificationEntry entry) { - try { - int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; - flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; - mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); - } - mShadeController.collapsePanel(true); - if (entry.getRow() != null) { - entry.getRow().updateBubbleButton(); - } - } - /** * Adds or updates a bubble associated with the provided notification entry. * * @param notif the notification associated with this bubble. */ - void updateBubble(NotificationEntry notif) { + @VisibleForTesting + public void updateBubble(BubbleEntry notif) { updateBubble(notif, false /* suppressFlyout */, true /* showInShade */); } @@ -1144,27 +693,32 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return; } bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble), - mContext, mStackView, mBubbleIconFactory, true /* skipInflation */); + mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */); }); return null; }); } - void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { + /** + * Adds or updates a bubble associated with the provided notification entry. + * + * @param notif the notification associated with this bubble. + * @param suppressFlyout this bubble suppress flyout or not. + * @param showInShade this bubble show in shade or not. + */ + @VisibleForTesting + public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) { // If this is an interruptive notif, mark that it's interrupted - if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { - notif.setInterruption(); - } + mSysuiProxy.setNotificationInterruption(notif.getKey()); if (!notif.getRanking().visuallyInterruptive() && (notif.getBubbleMetadata() != null && !notif.getBubbleMetadata().getAutoExpandBubble()) && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { // Update the bubble but don't promote it out of overflow Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); - b.setEntry(notifToBubbleEntry(notif)); + b.setEntry(notif); } else { - Bubble bubble = mBubbleData.getOrCreateBubble( - notifToBubbleEntry(notif), null /* persistedBubble */); + Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); inflateAndAdd(bubble, suppressFlyout, showInShade); } } @@ -1174,68 +728,33 @@ public class BubbleController implements Bubbles, ConfigurationController.Config ensureStackViewCreated(); bubble.setInflateSynchronously(mInflateSynchronously); bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade), - mContext, mStackView, mBubbleIconFactory, false /* skipInflation */); - } - - @Override - public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) { - NotificationChannel channel = entry.getChannel(); - final String appPkg = entry.getSbn().getPackageName(); - final int appUid = entry.getSbn().getUid(); - if (channel == null || appPkg == null) { - return; - } - - // Update the state in NotificationManagerService - try { - int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; - flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; - mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags); - } catch (RemoteException e) { - } - - // Change the settings - channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext, - mINotificationManager, entry, channel); - channel.setAllowBubbles(shouldBubble); - try { - int currentPref = mINotificationManager.getBubblePreferenceForPackage(appPkg, appUid); - if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) { - mINotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED); - } - mINotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); - } - - if (shouldBubble) { - mShadeController.collapsePanel(true); - if (entry.getRow() != null) { - entry.getRow().updateBubbleButton(); - } - } + mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); } + /** + * Removes the bubble with the given key. + * <p> + * Must be called from the main thread. + */ + @VisibleForTesting @MainThread - @Override public void removeBubble(String key, int reason) { if (mBubbleData.hasAnyBubbleWithKey(key)) { mBubbleData.dismissBubbleWithKey(key, reason); } } - private void onEntryAdded(NotificationEntry entry) { - if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) - && entry.isBubble() - && canLaunchInActivityView(mContext, entry)) { + @Override + public void onEntryAdded(BubbleEntry entry) { + if (canLaunchInActivityView(mContext, entry)) { updateBubble(entry); } } - private void onEntryUpdated(NotificationEntry entry) { + @Override + public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) { // shouldBubbleUp checks canBubble & for bubble metadata - boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) - && canLaunchInActivityView(mContext, entry); + boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); @@ -1244,9 +763,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - private void onEntryRemoved(NotificationEntry entry) { + @Override + public void onEntryRemoved(BubbleEntry entry) { if (isSummaryOfBubbles(entry)) { - final String groupKey = entry.getSbn().getGroupKey(); + final String groupKey = entry.getStatusBarNotification().getGroupKey(); mBubbleData.removeSuppressedSummary(groupKey); // Remove any associated bubble children with the summary @@ -1259,39 +779,30 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - /** - * Called when NotificationListener has received adjusted notification rank and reapplied - * filtering and sorting. This is used to dismiss or create bubbles based on changes in - * permissions on the notification channel or the global setting. - * - * @param rankingMap the updated ranking map from NotificationListenerService - */ - private void onRankingUpdated(RankingMap rankingMap) { + @Override + public void onRankingUpdated(RankingMap rankingMap) { if (mTmpRanking == null) { mTmpRanking = new NotificationListenerService.Ranking(); } String[] orderedKeys = rankingMap.getOrderedKeys(); for (int i = 0; i < orderedKeys.length; i++) { String key = orderedKeys[i]; - NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); + BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(key); rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !mTmpRanking.canBubble()) { // If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason. // This means that the app or channel's ability to bubble has been revoked. - mBubbleData.dismissBubbleWithKey( - key, BubbleController.DISMISS_BLOCKED); - } else if (isActiveBubble - && !mNotificationInterruptStateProvider.shouldBubbleUp(entry)) { + mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED); + } else if (isActiveBubble && !mSysuiProxy.shouldBubbleUp(key)) { // If this entry is allowed to bubble, but cannot currently bubble up, dismiss it. // This happens when DND is enabled and configured to hide bubbles. Dismissing with // the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that // the bubble will be re-created if shouldBubbleUp returns true. - mBubbleData.dismissBubbleWithKey( - key, BubbleController.DISMISS_NO_BUBBLE_UP); + mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { entry.setFlagBubble(true); - onEntryUpdated(entry); + onEntryUpdated(entry, true /* shouldBubbleUp */); } } } @@ -1306,23 +817,18 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return bubbleChildren; } for (Bubble bubble : mBubbleData.getActiveBubbles()) { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey()); - if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { + final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey()); + if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) { bubbleChildren.add(bubble); } } return bubbleChildren; } - private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble, + private void setIsBubble(@NonNull final BubbleEntry entry, final boolean isBubble, final boolean autoExpand) { Objects.requireNonNull(entry); - if (isBubble) { - entry.getSbn().getNotification().flags |= FLAG_BUBBLE; - } else { - entry.getSbn().getNotification().flags &= ~FLAG_BUBBLE; - } + entry.setFlagBubble(isBubble); try { int flags = 0; if (autoExpand) { @@ -1338,8 +844,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) { Objects.requireNonNull(b); b.setIsBubble(isBubble); - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(b.getKey()); + final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(b.getKey()); if (entry != null) { // Updating the entry to be a bubble will trigger our normal update flow setIsBubble(entry, isBubble, b.shouldAutoExpand()); @@ -1372,7 +877,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config // Collapsing? Do this first before remaining steps. if (update.expandedChanged && !update.expanded) { mStackView.setExpanded(false); - mNotificationShadeWindowController.setRequestTopUi(false, TAG); + mSysuiProxy.requestNotificationShadeTopUi(false, TAG); } // Do removals, if any. @@ -1396,8 +901,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config if (reason == DISMISS_NOTIF_CANCEL) { bubblesToBeRemovedFromRepository.add(bubble); } - final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( - bubble.getKey()); if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey()) && (!bubble.showInShade() @@ -1405,31 +908,21 @@ public class BubbleController implements Bubbles, ConfigurationController.Config || reason == DISMISS_GROUP_CANCELLED)) { // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it - for (NotifCallback cb : mCallbacks) { - if (entry != null) { - cb.removeNotification( - entry, - getDismissedByUserStats(entry, true), - REASON_CANCEL); - } - } + mSysuiProxy.notifyRemoveNotification(bubble.getKey(), REASON_CANCEL); } else { if (bubble.isBubble()) { setIsBubble(bubble, false /* isBubble */); } - if (entry != null && entry.getRow() != null) { - entry.getRow().updateBubbleButton(); - } + mSysuiProxy.updateNotificationBubbleButton(bubble.getKey()); } } + final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey()); if (entry != null) { - final String groupKey = entry.getSbn().getGroupKey(); + final String groupKey = entry.getStatusBarNotification().getGroupKey(); if (getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary - for (NotifCallback cb : mCallbacks) { - cb.maybeCancelSummary(entry); - } + mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey()); } } } @@ -1454,11 +947,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config if (update.selectionChanged && mStackView != null) { mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null) { - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(update.selectedBubble.getKey()); - if (entry != null) { - mNotificationGroupManager.updateSuppression(entry); - } + mSysuiProxy.updateNotificationSuppression(update.selectedBubble.getKey()); } } @@ -1466,24 +955,20 @@ public class BubbleController implements Bubbles, ConfigurationController.Config if (update.expandedChanged && update.expanded) { if (mStackView != null) { mStackView.setExpanded(true); - mNotificationShadeWindowController.setRequestTopUi(true, TAG); + mSysuiProxy.requestNotificationShadeTopUi(true, TAG); } } - for (NotifCallback cb : mCallbacks) { - cb.invalidateNotifications("BubbleData.Listener.applyUpdate"); - } + mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate"); updateStack(); } }; @Override - public boolean handleDismissalInterception(NotificationEntry entry) { - if (entry == null) { - return false; - } + public boolean handleDismissalInterception(BubbleEntry entry, + @Nullable List<BubbleEntry> children, IntConsumer removeCallback) { if (isSummaryOfBubbles(entry)) { - handleSummaryDismissalInterception(entry); + handleSummaryDismissalInterception(entry, children, removeCallback); } else { Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey()); if (bubble == null || !entry.isBubble()) { @@ -1496,87 +981,50 @@ public class BubbleController implements Bubbles, ConfigurationController.Config bubble.setShowDot(false /* show */); } // Update the shade - for (NotifCallback cb : mCallbacks) { - cb.invalidateNotifications("BubbleController.handleDismissalInterception"); - } + mSysuiProxy.notifyInvalidateNotifications("BubbleController.handleDismissalInterception"); return true; } - private boolean isSummaryOfBubbles(NotificationEntry entry) { - if (entry == null) { - return false; - } - - String groupKey = entry.getSbn().getGroupKey(); + private boolean isSummaryOfBubbles(BubbleEntry entry) { + String groupKey = entry.getStatusBarNotification().getGroupKey(); ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); - boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); - return (isSuppressedSummary || isSummary) - && bubbleChildren != null - && !bubbleChildren.isEmpty(); + boolean isSummary = entry.getStatusBarNotification().getNotification().isGroupSummary(); + return (isSuppressedSummary || isSummary) && !bubbleChildren.isEmpty(); } - private void handleSummaryDismissalInterception(NotificationEntry summary) { - // current children in the row: - final List<NotificationEntry> children = summary.getAttachedNotifChildren(); + private void handleSummaryDismissalInterception( + BubbleEntry summary, @Nullable List<BubbleEntry> children, IntConsumer removeCallback) { if (children != null) { for (int i = 0; i < children.size(); i++) { - NotificationEntry child = children.get(i); + BubbleEntry child = children.get(i); if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) { // Suppress the bubbled child // As far as group manager is concerned, once a child is no longer shown // in the shade, it is essentially removed. Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey()); if (bubbleChild != null) { - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(bubbleChild.getKey()); - if (entry != null) { - mNotificationGroupManager.onEntryRemoved(entry); - } + mSysuiProxy.removeNotificationEntry(bubbleChild.getKey()); bubbleChild.setSuppressNotification(true); bubbleChild.setShowDot(false /* show */); } } else { // non-bubbled children can be removed - for (NotifCallback cb : mCallbacks) { - cb.removeNotification( - child, - getDismissedByUserStats(child, true), - REASON_GROUP_SUMMARY_CANCELED); - } + removeCallback.accept(i); } } } // And since all children are removed, remove the summary. - mNotificationGroupManager.onEntryRemoved(summary); + removeCallback.accept(-1); // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated - mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(), + mBubbleData.addSummaryToSuppress(summary.getStatusBarNotification().getGroupKey(), summary.getKey()); } /** - * Gets the DismissedByUserStats used by {@link NotificationEntryManager}. - * Will not be necessary when using the new notification pipeline's {@link NotifCollection}. - * Instead, this is taken care of by {@link BubbleCoordinator}. - */ - private DismissedByUserStats getDismissedByUserStats( - NotificationEntry entry, - boolean isVisible) { - return new DismissedByUserStats( - DISMISSAL_BUBBLE, - DISMISS_SENTIMENT_NEUTRAL, - NotificationVisibility.obtain( - entry.getKey(), - entry.getRanking().getRank(), - mNotificationEntryManager.getActiveNotificationsCount(), - isVisible, - NotificationLogger.getNotificationLocation(entry))); - } - - /** * Updates the visibility of the bubbles based on current state. * Does not un-bubble, just hides or un-hides. * Updates stack description for TalkBack focus. @@ -1586,7 +1034,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return; } - if (mStatusBarStateListener.getCurrentState() != SHADE) { + if (!mIsStatusBarShade) { // Bubbles don't appear over the locked shade. mStackView.setVisibility(INVISIBLE); } else if (hasBubbles()) { @@ -1610,20 +1058,21 @@ public class BubbleController implements Bubbles, ConfigurationController.Config final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble(); if (expandedViewProvider != null && isStackExpanded() && !mStackView.isExpansionAnimating() - && !mNotificationShadeWindowController.getPanelExpanded()) { + && !mSysuiProxy.isNotificationShadeExpand()) { return expandedViewProvider.getTaskId(); } return INVALID_TASK_ID; } @VisibleForTesting - BubbleStackView getStackView() { + public BubbleStackView getStackView() { return mStackView; } /** * Description of current bubble state. */ + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("BubbleController state:"); mBubbleData.dump(fd, pw, args); @@ -1635,39 +1084,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } /** - * This task stack listener is responsible for responding to tasks moved to the front - * which are on the default (main) display. When this happens, expanded bubbles must be - * collapsed so the user may interact with the app which was just moved to the front. - * <p> - * This listener is registered with SystemUI's ActivityManagerWrapper which dispatches - * these calls via a main thread Handler. - */ - @MainThread - private class BubbleTaskStackListener extends TaskStackChangeListener { - - @Override - public void onTaskMovedToFront(RunningTaskInfo taskInfo) { - int expandedId = getExpandedTaskId(); - if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) { - mBubbleData.setExpanded(false); - } - } - - @Override - public void onActivityRestartAttempt(RunningTaskInfo taskInfo, boolean homeTaskVisible, - boolean clearedTask, boolean wasVisible) { - for (Bubble b : mBubbleData.getBubbles()) { - if (taskInfo.taskId == b.getTaskId()) { - mBubbleData.setSelectedBubble(b); - mBubbleData.setExpanded(true); - return; - } - } - } - - } - - /** * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. * * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically @@ -1676,7 +1092,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * @param context the context to use. * @param entry the entry to bubble. */ - static boolean canLaunchInActivityView(Context context, NotificationEntry entry) { + static boolean canLaunchInActivityView(Context context, BubbleEntry entry) { PendingIntent intent = entry.getBubbleMetadata() != null ? entry.getBubbleMetadata().getIntent() : null; @@ -1688,8 +1104,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey()); return false; } - PackageManager packageManager = StatusBar.getPackageManagerForUser( - context, entry.getSbn().getUser().getIdentifier()); + PackageManager packageManager = getPackageManagerForUser( + context, entry.getStatusBarNotification().getUser().getIdentifier()); ActivityInfo info = intent.getIntent().resolveActivityInfo(packageManager, 0); if (info == null) { @@ -1707,6 +1123,24 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return true; } + static PackageManager getPackageManagerForUser(Context context, int userId) { + Context contextForUser = context; + // UserHandle defines special userId as negative values, e.g. USER_ALL + if (userId >= 0) { + try { + // Create a context for the correct user so if a package isn't installed + // for user 0 we can still load information about the package. + contextForUser = + context.createPackageContextAsUser(context.getPackageName(), + Context.CONTEXT_RESTRICTED, + new UserHandle(userId)); + } catch (PackageManager.NameNotFoundException e) { + // Shouldn't fail to find the package name for system ui. + } + } + return contextForUser.getPackageManager(); + } + /** PinnedStackListener that dispatches IME visibility updates to the stack. */ //TODO(b/170442945): Better way to do this / insets listener? private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener { @@ -1717,10 +1151,4 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } } - - static BubbleEntry notifToBubbleEntry(NotificationEntry e) { - return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), - e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), - e.shouldSuppressPeek()); - } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index b4626f27d370..8cacc8f2ef01 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -34,7 +34,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.systemui.R; -import com.android.systemui.bubbles.BubbleController.DismissReason; +import com.android.systemui.bubbles.Bubbles.DismissReason; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -137,8 +137,8 @@ public class BubbleData { private Listener mListener; @Nullable - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; - private BubbleController.PendingIntentCanceledListener mCancelledListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.PendingIntentCanceledListener mCancelledListener; /** * We track groups with summaries that aren't visibly displayed but still kept around because @@ -165,12 +165,12 @@ public class BubbleData { } public void setSuppressionChangedListener( - BubbleController.NotificationSuppressionChangedListener listener) { + Bubbles.NotificationSuppressionChangedListener listener) { mSuppressionListener = listener; } public void setPendingIntentCancelledListener( - BubbleController.PendingIntentCanceledListener listener) { + Bubbles.PendingIntentCanceledListener listener) { mCancelledListener = listener; } @@ -344,7 +344,8 @@ public class BubbleData { /** * Whether the summary for the provided group key is suppressed. */ - boolean isSummarySuppressed(String groupKey) { + @VisibleForTesting + public boolean isSummarySuppressed(String groupKey) { return mSuppressedGroupKeys.containsKey(groupKey); } @@ -415,7 +416,7 @@ public class BubbleData { // skip the selected bubble .filter((b) -> !b.equals(mSelectedBubble)) .findFirst() - .ifPresent((b) -> doRemove(b.getKey(), BubbleController.DISMISS_AGED)); + .ifPresent((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED)); } } @@ -459,12 +460,12 @@ public class BubbleData { int indexToRemove = indexForKey(key); if (indexToRemove == -1) { if (hasOverflowBubbleWithKey(key) - && (reason == BubbleController.DISMISS_NOTIF_CANCEL - || reason == BubbleController.DISMISS_GROUP_CANCELLED - || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE - || reason == BubbleController.DISMISS_BLOCKED - || reason == BubbleController.DISMISS_SHORTCUT_REMOVED - || reason == BubbleController.DISMISS_PACKAGE_REMOVED)) { + && (reason == Bubbles.DISMISS_NOTIF_CANCEL + || reason == Bubbles.DISMISS_GROUP_CANCELLED + || reason == Bubbles.DISMISS_NO_LONGER_BUBBLE + || reason == Bubbles.DISMISS_BLOCKED + || reason == Bubbles.DISMISS_SHORTCUT_REMOVED + || reason == Bubbles.DISMISS_PACKAGE_REMOVED)) { Bubble b = getOverflowBubbleWithKey(key); if (DEBUG_BUBBLE_DATA) { @@ -512,8 +513,8 @@ public class BubbleData { void overflowBubble(@DismissReason int reason, Bubble bubble) { if (bubble.getPendingIntentCanceled() - || !(reason == BubbleController.DISMISS_AGED - || reason == BubbleController.DISMISS_USER_GESTURE)) { + || !(reason == Bubbles.DISMISS_AGED + || reason == Bubbles.DISMISS_USER_GESTURE)) { return; } if (DEBUG_BUBBLE_DATA) { @@ -529,7 +530,7 @@ public class BubbleData { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Overflow full. Remove: " + oldest); } - mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED); + mStateChange.bubbleRemoved(oldest, Bubbles.DISMISS_OVERFLOW_MAX_REACHED); mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED); mOverflowBubbles.remove(oldest); mStateChange.removedOverflowBubble = oldest; @@ -694,7 +695,7 @@ public class BubbleData { } private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) { - if (reason != BubbleController.DISMISS_USER_GESTURE) return; + if (reason != Bubbles.DISMISS_USER_GESTURE) return; PendingIntent deleteIntent = bubble.getDeleteIntent(); if (deleteIntent == null) return; try { @@ -726,7 +727,7 @@ public class BubbleData { * The set of bubbles in overflow. */ @VisibleForTesting(visibility = PRIVATE) - List<Bubble> getOverflowBubbles() { + public List<Bubble> getOverflowBubbles() { return Collections.unmodifiableList(mOverflowBubbles); } @@ -742,7 +743,7 @@ public class BubbleData { @VisibleForTesting(visibility = PRIVATE) @Nullable - Bubble getBubbleInStackWithKey(String key) { + public Bubble getBubbleInStackWithKey(String key) { for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); if (bubble.getKey().equals(key)) { @@ -764,7 +765,7 @@ public class BubbleData { } @VisibleForTesting(visibility = PRIVATE) - Bubble getOverflowBubbleWithKey(String key) { + public Bubble getOverflowBubbleWithKey(String key) { for (int i = 0; i < mOverflowBubbles.size(); i++) { Bubble bubble = mOverflowBubbles.get(i); if (bubble.getKey().equals(key)) { @@ -788,7 +789,7 @@ public class BubbleData { * This method should only be used in tests, not in production. */ @VisibleForTesting - void setMaxOverflowBubbles(int maxOverflowBubbles) { + public void setMaxOverflowBubbles(int maxOverflowBubbles) { mMaxOverflowBubbles = maxOverflowBubbles; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java index d98fee399470..3937422750cc 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java @@ -33,10 +33,10 @@ public class BubbleDebugConfig { // to figure-out the origin of a log message while debugging the Bubbles a little painful. By // setting this constant to true, log messages from the Bubbles package will be tagged with // their class names instead fot the generic tag. - static final boolean TAG_WITH_CLASS_NAME = false; + public static final boolean TAG_WITH_CLASS_NAME = false; // Default log tag for the Bubbles package. - static final String TAG_BUBBLES = "Bubbles"; + public static final String TAG_BUBBLES = "Bubbles"; static final boolean DEBUG_BUBBLE_CONTROLLER = false; static final boolean DEBUG_BUBBLE_DATA = false; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java index 6a1302518699..a0d3391f8347 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java @@ -16,6 +16,9 @@ package com.android.systemui.bubbles; +import static android.app.Notification.FLAG_BUBBLE; + +import android.app.Notification; import android.app.Notification.BubbleMetadata; import android.app.NotificationManager.Policy; import android.service.notification.NotificationListenerService.Ranking; @@ -67,12 +70,45 @@ public class BubbleEntry { return mSbn.getKey(); } + /** @return the group key in the {@link StatusBarNotification}. */ + public String getGroupKey() { + return mSbn.getGroupKey(); + } + /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */ @Nullable public BubbleMetadata getBubbleMetadata() { return getStatusBarNotification().getNotification().getBubbleMetadata(); } + /** + * Updates the {@link Notification#FLAG_BUBBLE} flag on this notification to indicate + * whether it is a bubble or not. If this entry is set to not bubble, or does not have + * the required info to bubble, the flag cannot be set to true. + * + * @param shouldBubble whether this notification should be flagged as a bubble. + * @return true if the value changed. + */ + public boolean setFlagBubble(boolean shouldBubble) { + boolean wasBubble = isBubble(); + if (!shouldBubble) { + mSbn.getNotification().flags &= ~FLAG_BUBBLE; + } else if (getBubbleMetadata() != null && canBubble()) { + // wants to be bubble & can bubble, set flag + mSbn.getNotification().flags |= FLAG_BUBBLE; + } + return wasBubble != isBubble(); + } + + public boolean isBubble() { + return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0; + } + + /** @see Ranking#canBubble() */ + public boolean canBubble() { + return mRanking.canBubble(); + } + /** @return true if this notification is clearable. */ public boolean isClearable() { return mIsClearable; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index cf2e13356cca..ae3c683cb165 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -54,7 +54,6 @@ import android.widget.LinearLayout; import androidx.annotation.Nullable; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.AlphaOptimizedButton; @@ -99,7 +98,7 @@ public class BubbleExpandedView extends LinearLayout { // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead private boolean mIsOverflow; - private Bubbles mBubbles = Dependency.get(Bubbles.class); + private BubbleController mController; private BubbleStackView mStackView; private BubblePositioner mPositioner; @@ -156,8 +155,7 @@ public class BubbleExpandedView extends LinearLayout { // the bubble again so we'll just remove it. Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() + ", " + e.getMessage() + "; removing bubble"); - mBubbles.removeBubble(getBubbleKey(), - BubbleController.DISMISS_INVALID_INTENT); + mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT); } }); mInitialized = true; @@ -195,15 +193,15 @@ public class BubbleExpandedView extends LinearLayout { } if (mBubble != null) { // Must post because this is called from a binder thread. - post(() -> mBubbles.removeBubble(mBubble.getKey(), - BubbleController.DISMISS_TASK_FINISHED)); + post(() -> mController.removeBubble( + mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED)); } } @Override public void onBackPressedOnTaskRoot(int taskId) { if (mTaskId == taskId && mStackView.isExpanded()) { - mBubbles.collapseStack(); + mController.collapseStack(); } } }; @@ -250,9 +248,6 @@ public class BubbleExpandedView extends LinearLayout { R.dimen.bubble_manage_button_height); mSettingsIcon = findViewById(R.id.settings_button); - mPositioner = mBubbles.getPositioner(); - - mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer()); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); @@ -263,7 +258,6 @@ public class BubbleExpandedView extends LinearLayout { } }); mExpandedViewContainer.setClipToOutline(true); - mExpandedViewContainer.addView(mTaskView); mExpandedViewContainer.setLayoutParams( new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); addView(mExpandedViewContainer); @@ -274,9 +268,7 @@ public class BubbleExpandedView extends LinearLayout { // ==> expanded view // ==> activity view // ==> manage button - bringChildToFront(mTaskView); bringChildToFront(mSettingsIcon); - mTaskView.setListener(mTaskViewListener); applyThemeAttrs(); @@ -314,6 +306,20 @@ public class BubbleExpandedView extends LinearLayout { super.onAttachedToWindow(); mTaskView.setExecutor(new HandlerExecutor(getHandler())); } + /** + * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need + * to be called after view inflate. + */ + void initialize(BubbleController controller, BubbleStackView stackView) { + mController = controller; + mStackView = stackView; + + mTaskView = new TaskView(mContext, mController.getTaskOrganizer()); + mExpandedViewContainer.addView(mTaskView); + bringChildToFront(mTaskView); + mTaskView.setListener(mTaskViewListener); + mPositioner = mController.getPositioner(); + } void updateDimensions() { Resources res = getResources(); @@ -464,16 +470,12 @@ public class BubbleExpandedView extends LinearLayout { return mTaskId; } - void setStackView(BubbleStackView stackView) { - mStackView = stackView; - } - public void setOverflow(boolean overflow) { mIsOverflow = overflow; Intent target = new Intent(mContext, BubbleOverflowActivity.class); Bundle extras = new Bundle(); - extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles)); + extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController)); target.putExtras(extras); mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, target, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java index 48c809d1b0a7..a24f5c2b54c5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java @@ -91,14 +91,14 @@ public class BubbleLogger { * @param b Bubble removed from overflow * @param r Reason that bubble was removed */ - public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) { - if (r == BubbleController.DISMISS_NOTIF_CANCEL) { + public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) { + if (r == Bubbles.DISMISS_NOTIF_CANCEL) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL); - } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) { + } else if (r == Bubbles.DISMISS_GROUP_CANCELLED) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL); - } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) { + } else if (r == Bubbles.DISMISS_NO_LONGER_BUBBLE) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE); - } else if (r == BubbleController.DISMISS_BLOCKED) { + } else if (r == Bubbles.DISMISS_BLOCKED) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED); } } @@ -107,10 +107,10 @@ public class BubbleLogger { * @param b Bubble added to overflow * @param r Reason that bubble was added to overflow */ - public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) { - if (r == BubbleController.DISMISS_AGED) { + public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) { + if (r == Bubbles.DISMISS_AGED) { log(b, Event.BUBBLE_OVERFLOW_ADD_AGED); - } else if (r == BubbleController.DISMISS_USER_GESTURE) { + } else if (r == Bubbles.DISMISS_USER_GESTURE) { log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt index 102055de2bea..297144a86143 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt @@ -35,6 +35,7 @@ import com.android.systemui.R class BubbleOverflow( private val context: Context, + private val controller: BubbleController, private val stack: BubbleStackView ) : BubbleViewProvider { @@ -56,8 +57,8 @@ class BubbleOverflow( init { updateResources() with(expandedView) { + initialize(controller, stack) setOverflow(true) - setStackView(stack) applyThemeAttrs() } with(overflowBtn) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index fc3f5b6cbf5e..bc841730833c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -63,7 +63,7 @@ public class BubbleOverflowActivity extends Activity { private TextView mEmptyStateTitle; private TextView mEmptyStateSubtitle; private ImageView mEmptyStateImage; - private Bubbles mBubbles; + private BubbleController mController; private BubbleOverflowAdapter mAdapter; private RecyclerView mRecyclerView; private List<Bubble> mOverflowBubbles = new ArrayList<>(); @@ -111,7 +111,7 @@ public class BubbleOverflowActivity extends Activity { if (intent != null && intent.getExtras() != null) { IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER); if (binder instanceof ObjectWrapper) { - mBubbles = ((ObjectWrapper<Bubbles>) binder).get(); + mController = ((ObjectWrapper<BubbleController>) binder).get(); } } else { Log.w(TAG, "Bubble overflow activity created without bubble controller!"); @@ -139,15 +139,15 @@ public class BubbleOverflowActivity extends Activity { final int viewHeight = recyclerViewHeight / rows; mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles, - mBubbles::promoteBubbleFromOverflow, viewWidth, viewHeight); + mController::promoteBubbleFromOverflow, viewWidth, viewHeight); mRecyclerView.setAdapter(mAdapter); mOverflowBubbles.clear(); - mOverflowBubbles.addAll(mBubbles.getOverflowBubbles()); + mOverflowBubbles.addAll(mController.getOverflowBubbles()); mAdapter.notifyDataSetChanged(); updateEmptyStateVisibility(); - mBubbles.setOverflowListener(mDataListener); + mController.setOverflowListener(mDataListener); updateTheme(); } @@ -217,7 +217,7 @@ public class BubbleOverflowActivity extends Activity { if (DEBUG_OVERFLOW) { Log.d(TAG, BubbleDebugConfig.formatBubblesString( - mBubbles.getOverflowBubbles(), null)); + mController.getOverflowBubbles(), null)); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 1201d42b1fc5..69ed5b72c1b2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -58,7 +58,6 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -155,7 +154,7 @@ public class BubbleStackView extends FrameLayout * * {@hide} */ - interface SurfaceSynchronizer { + public interface SurfaceSynchronizer { /** * Wait until requested change on a {@link View} is reflected on the screen. * @@ -186,7 +185,7 @@ public class BubbleStackView extends FrameLayout }); } }; - + private final BubbleController mBubbleController; private final BubbleData mBubbleData; private final ValueAnimator mDesaturateAndDarkenAnimator; @@ -384,22 +383,6 @@ public class BubbleStackView extends FrameLayout private final SurfaceSynchronizer mSurfaceSynchronizer; /** - * Callback to run when the IME visibility changes - BubbleController uses this to update the - * Bubbles window focusability flags with the WindowManager. - */ - public final Consumer<Boolean> mOnImeVisibilityChanged; - - /** - * Callback to run when the bubble expand status changes. - */ - private final Consumer<Boolean> mOnBubbleExpandChanged; - - /** - * Callback to run to ask BubbleController to hide the current IME. - */ - private final Runnable mHideCurrentInputMethodCallback; - - /** * The currently magnetized object, which is being dragged and will be attracted to the magnetic * dismiss target. * @@ -733,16 +716,12 @@ public class BubbleStackView extends FrameLayout private BubblePositioner mPositioner; @SuppressLint("ClickableViewAccessibility") - public BubbleStackView(Context context, BubbleData data, - @Nullable SurfaceSynchronizer synchronizer, - FloatingContentCoordinator floatingContentCoordinator, - Runnable allBubblesAnimatedOutAction, - Consumer<Boolean> onImeVisibilityChanged, - Runnable hideCurrentInputMethodCallback, - Consumer<Boolean> onBubbleExpandChanged, - BubblePositioner positioner) { + public BubbleStackView(Context context, BubbleController bubbleController, + BubbleData data, @Nullable SurfaceSynchronizer synchronizer, + FloatingContentCoordinator floatingContentCoordinator) { super(context); + mBubbleController = bubbleController; mBubbleData = data; Resources res = getResources(); @@ -755,7 +734,7 @@ public class BubbleStackView extends FrameLayout mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mPositioner = positioner; + mPositioner = mBubbleController.getPositioner(); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); @@ -767,7 +746,7 @@ public class BubbleStackView extends FrameLayout final Runnable onBubbleAnimatedOut = () -> { if (getBubbleCount() == 0) { - allBubblesAnimatedOutAction.run(); + mBubbleController.onAllBubblesAnimatedOut(); } }; @@ -839,7 +818,7 @@ public class BubbleStackView extends FrameLayout setFocusable(true); mBubbleContainer.bringToFront(); - mBubbleOverflow = new BubbleOverflow(getContext(), this); + mBubbleOverflow = new BubbleOverflow(getContext(), bubbleController, this); mBubbleContainer.addView(mBubbleOverflow.getIconView(), mBubbleContainer.getChildCount() /* index */, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, @@ -850,12 +829,9 @@ public class BubbleStackView extends FrameLayout showManageMenu(false); }); - mOnImeVisibilityChanged = onImeVisibilityChanged; - mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback; - mOnBubbleExpandChanged = onBubbleExpandChanged; - setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { - onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0); + mBubbleController.onImeVisibilityChanged( + insets.getInsets(WindowInsets.Type.ime()).bottom > 0); if (!mIsExpanded || mIsExpansionAnimating) { return view.onApplyWindowInsets(insets); } @@ -1112,9 +1088,6 @@ public class BubbleStackView extends FrameLayout } mFlyout = new BubbleFlyoutView(getContext()); mFlyout.setVisibility(GONE); - mFlyout.animate() - .setDuration(FLYOUT_ALPHA_ANIMATION_DURATION) - .setInterpolator(new AccelerateDecelerateInterpolator()); mFlyout.setOnClickListener(mFlyoutClickListener); mFlyout.setOnTouchListener(mFlyoutTouchListener); addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); @@ -1299,7 +1272,7 @@ public class BubbleStackView extends FrameLayout // R constants are not final so we cannot use switch-case here. if (action == AccessibilityNodeInfo.ACTION_DISMISS) { - mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); + mBubbleData.dismissAll(Bubbles.DISMISS_ACCESSIBILITY_ACTION); announceForAccessibility( getResources().getString(R.string.accessibility_bubble_dismissed)); return true; @@ -1402,8 +1375,9 @@ public class BubbleStackView extends FrameLayout /** * The {@link Bubble} that is expanded, null if one does not exist. */ + @VisibleForTesting @Nullable - BubbleViewProvider getExpandedBubble() { + public BubbleViewProvider getExpandedBubble() { return mExpandedBubble; } @@ -1617,7 +1591,7 @@ public class BubbleStackView extends FrameLayout hideCurrentInputMethod(); - mOnBubbleExpandChanged.accept(shouldExpand); + mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand); if (mIsExpanded) { animateCollapse(); @@ -1637,7 +1611,7 @@ public class BubbleStackView extends FrameLayout * not. */ void hideCurrentInputMethod() { - mHideCurrentInputMethodCallback.run(); + mBubbleController.hideCurrentInputMethod(); } private void beforeExpandedViewAnimation() { @@ -2135,14 +2109,13 @@ public class BubbleStackView extends FrameLayout final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject(); dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView)); } else { - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); } } private void dismissBubbleIfExists(@Nullable Bubble bubble) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { - mBubbleData.dismissBubbleWithKey( - bubble.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(bubble.getKey(), Bubbles.DISMISS_USER_GESTURE); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java index 010a29e3560a..a3e6a1ecc387 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java @@ -66,6 +66,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask private Bubble mBubble; private WeakReference<Context> mContext; + private WeakReference<BubbleController> mController; private WeakReference<BubbleStackView> mStackView; private BubbleIconFactory mIconFactory; private boolean mSkipInflation; @@ -77,12 +78,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask */ BubbleViewInfoTask(Bubble b, Context context, + BubbleController controller, BubbleStackView stackView, BubbleIconFactory factory, boolean skipInflation, Callback c) { mBubble = b; mContext = new WeakReference<>(context); + mController = new WeakReference<>(controller); mStackView = new WeakReference<>(stackView); mIconFactory = factory; mSkipInflation = skipInflation; @@ -91,8 +94,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @Override protected BubbleViewInfo doInBackground(Void... voids) { - return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble, - mSkipInflation); + return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(), + mIconFactory, mBubble, mSkipInflation); } @Override @@ -121,8 +124,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask Bubble.FlyoutMessage flyoutMessage; @Nullable - static BubbleViewInfo populate(Context c, BubbleStackView stackView, - BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) { + static BubbleViewInfo populate(Context c, BubbleController controller, + BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b, + boolean skipInflation) { BubbleViewInfo info = new BubbleViewInfo(); // View inflation: only should do this once per bubble @@ -133,7 +137,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask info.expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); - info.expandedView.setStackView(stackView); + info.expandedView.initialize(controller, stackView); } if (b.getShortcutInfo() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java index 5cc24ce5a775..589017242311 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java @@ -26,7 +26,7 @@ import androidx.annotation.Nullable; /** * Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow. */ -interface BubbleViewProvider { +public interface BubbleViewProvider { @Nullable BubbleExpandedView getExpandedView(); void setContentVisibility(boolean visible); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java index 4882abc51ed6..415edb1b647c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java @@ -16,62 +16,93 @@ package com.android.systemui.bubbles; -import android.annotation.NonNull; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; -import androidx.annotation.MainThread; +import android.content.res.Configuration; +import android.service.notification.NotificationListenerService.RankingMap; +import android.util.ArraySet; +import android.view.View; -import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.wm.shell.ShellTaskOrganizer; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.List; +import java.util.function.IntConsumer; /** * Interface to engage bubbles feature. */ public interface Bubbles { + @Retention(SOURCE) + @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, + DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, + DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, + DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, + DISMISS_NO_BUBBLE_UP}) + @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) + @interface DismissReason {} + + int DISMISS_USER_GESTURE = 1; + int DISMISS_AGED = 2; + int DISMISS_TASK_FINISHED = 3; + int DISMISS_BLOCKED = 4; + int DISMISS_NOTIF_CANCEL = 5; + int DISMISS_ACCESSIBILITY_ACTION = 6; + int DISMISS_NO_LONGER_BUBBLE = 7; + int DISMISS_USER_CHANGED = 8; + int DISMISS_GROUP_CANCELLED = 9; + int DISMISS_INVALID_INTENT = 10; + int DISMISS_OVERFLOW_MAX_REACHED = 11; + int DISMISS_SHORTCUT_REMOVED = 12; + int DISMISS_PACKAGE_REMOVED = 13; + int DISMISS_NO_BUBBLE_UP = 14; + /** * @return {@code true} if there is a bubble associated with the provided key and if its * notification is hidden from the shade or there is a group summary associated with the * provided key that is hidden from the shade because it has been dismissed but still has child * bubbles active. */ - boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry); + boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey); /** * @return {@code true} if the current notification entry same as selected bubble * notification entry and the stack is currently expanded. */ - boolean isBubbleExpanded(NotificationEntry entry); + boolean isBubbleExpanded(String key); /** @return {@code true} if stack of bubbles is expanded or not. */ boolean isStackExpanded(); + /** @return {@code true} if the summary for the provided group key is suppressed. */ + boolean isSummarySuppressed(String groupKey); + /** - * @return the {@link ScrimView} drawn behind the bubble stack. This is managed by - * {@link ScrimController} since we want the scrim's appearance and behavior to be identical to - * that of the notification shade scrim. + * Removes a group key indicating that summary for this group should no longer be suppressed. */ - ScrimView getScrimForBubble(); - - /** @return Bubbles for updating overflow. */ - List<Bubble> getOverflowBubbles(); + void removeSuppressedSummary(String groupKey); /** Tell the stack of bubbles to collapse. */ void collapseStack(); + /** Tell the controller need update its UI to fit theme. */ + void updateForThemeChanges(); + /** * Request the stack expand if needed, then select the specified Bubble as current. * If no bubble exists for this entry, one is created. * * @param entry the notification for the bubble to be selected */ - void expandStackAndSelectBubble(NotificationEntry entry); - - /** Promote the provided bubbles when overflow view. */ - void promoteBubbleFromOverflow(Bubble bubble); + void expandStackAndSelectBubble(BubbleEntry entry); /** * We intercept notification entries (including group summaries) dismissed by the user when @@ -81,26 +112,65 @@ public interface Bubbles { * {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add * {@link BubbleData#addSummaryToSuppress}. * + * @param entry the notification of the BubbleEntry should be removed. + * @param children the list of child notification of the BubbleEntry from 1st param entry, + * this will be null if entry does have no children. + * @param removeCallback the remove callback for SystemUI side to remove notification, the int + * number should be list position of children list and use -1 for + * removing the parent notification. + * * @return true if we want to intercept the dismissal of the entry, else false. */ - boolean handleDismissalInterception(NotificationEntry entry); + boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children, + IntConsumer removeCallback); /** - * Removes the bubble with the given key. - * <p> - * Must be called from the main thread. + * Retrieves the notif entry key of the summary associated with the provided group key. + * + * @param groupKey the group to look up + * @return the key for the notification that is the summary of this group. */ - @MainThread - void removeBubble(String key, int reason); + String getSummaryKey(String groupKey); + + /** Set the proxy to commnuicate with SysUi side components. */ + void setSysuiProxy(SysuiProxy proxy); + + /** Set the scrim view for bubbles. */ + void setBubbleScrim(View view); + + /** Set a listener to be notified of bubble expand events. */ + void setExpandListener(BubbleExpandListener listener); + /** + * Called when new notification entry added. + * + * @param entry the {@link BubbleEntry} by the notification. + */ + void onEntryAdded(BubbleEntry entry); + + /** + * Called when new notification entry updated. + * + * @param entry the {@link BubbleEntry} by the notification. + * @param shouldBubbleUp {@code true} if this notification should bubble up. + */ + void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp); + + /** + * Called when new notification entry removed. + * + * @param entry the {@link BubbleEntry} by the notification. + */ + void onEntryRemoved(BubbleEntry entry); /** - * When a notification is marked Priority, expand the stack if needed, - * then (maybe create and) select the given bubble. + * Called when NotificationListener has received adjusted notification rank and reapplied + * filtering and sorting. This is used to dismiss or create bubbles based on changes in + * permissions on the notification channel or the global setting. * - * @param entry the notification for the bubble to show + * @param rankingMap the updated ranking map from NotificationListenerService */ - void onUserChangedImportance(NotificationEntry entry); + void onRankingUpdated(RankingMap rankingMap); /** * Called when the status bar has become visible or invisible (either permanently or @@ -108,30 +178,85 @@ public interface Bubbles { */ void onStatusBarVisibilityChanged(boolean visible); + /** Called when system zen mode state changed. */ + void onZenStateChanged(); + /** - * Called when a user has indicated that an active notification should be shown as a bubble. - * <p> - * This method will collapse the shade, create the bubble without a flyout or dot, and suppress - * the notification from appearing in the shade. + * Called when statusBar state changed. * - * @param entry the notification to change bubble state for. - * @param shouldBubble whether the notification should show as a bubble or not. + * @param isShade {@code true} is state is SHADE. */ - void onUserChangedBubble(@NonNull NotificationEntry entry, boolean shouldBubble); + void onStatusBarStateChanged(boolean isShade); + /** + * Called when the current user changed. + * + * @param newUserId the new user's id. + */ + void onUserChanged(int newUserId); - /** See {@link BubbleController.NotifCallback}. */ - void addNotifCallback(BubbleController.NotifCallback callback); + /** + * Called when config changed. + * + * @param newConfig the new config. + */ + void onConfigChanged(Configuration newConfig); - /** Set a listener to be notified of bubble expand events. */ - void setExpandListener(BubbleController.BubbleExpandListener listener); + /** Description of current bubble state. */ + void dump(FileDescriptor fd, PrintWriter pw, String[] args); + + /** Listener to find out about stack expansion / collapse events. */ + interface BubbleExpandListener { + /** + * Called when the expansion state of the bubble stack changes. + * + * @param isExpanding whether it's expanding or collapsing + * @param key the notification key associated with bubble being expanded + */ + void onBubbleExpandChanged(boolean isExpanding, String key); + } + + /** Listener to be notified when a bubbles' notification suppression state changes.*/ + interface NotificationSuppressionChangedListener { + /** Called when the notification suppression state of a bubble changes. */ + void onBubbleNotificationSuppressionChange(Bubble bubble); + } + + /** Listener to be notified when a pending intent has been canceled for a bubble. */ + interface PendingIntentCanceledListener { + /** Called when the pending intent for a bubble has been canceled. */ + void onPendingIntentCanceled(Bubble bubble); + } + + /** Callback to tell SysUi components execute some methods. */ + interface SysuiProxy { + @Nullable + BubbleEntry getPendingOrActiveEntry(String key); + + List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys); + + boolean isNotificationShadeExpand(); + + boolean shouldBubbleUp(String key); + + void setNotificationInterruption(String key); + + void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag); + + void notifyRemoveNotification(String key, int reason); + + void notifyInvalidateNotifications(String reason); + + void notifyMaybeCancelSummary(String key); + + void removeNotificationEntry(String key); + + void updateNotificationBubbleButton(String key); - /** Set a listener to be notified of when overflow view update. */ - void setOverflowListener(BubbleData.Listener listener); + void updateNotificationSuppression(String key); - /** The task listener for events in bubble tasks. **/ - ShellTaskOrganizer getTaskOrganizer(); + void onStackExpandChanged(boolean shouldExpand); - /** Contains information to help position things on the screen. */ - BubblePositioner getPositioner(); + void onUnbubbleConversation(String key); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index 6e6f82b714ff..0a596d5c69b6 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -27,6 +27,7 @@ import android.util.FloatProperty; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; import androidx.annotation.Nullable; @@ -385,7 +386,7 @@ public class PhysicsAnimationLayout extends FrameLayout { View view, DynamicAnimation.ViewProperty... properties) { final ObjectAnimator targetAnimator = getTargetAnimatorFromView(view); for (DynamicAnimation.ViewProperty property : properties) { - final SpringAnimation animation = getAnimationFromView(property, view); + final SpringAnimation animation = getSpringAnimationFromView(property, view); if (animation != null && animation.isRunning()) { return true; } @@ -422,11 +423,15 @@ public class PhysicsAnimationLayout extends FrameLayout { for (int i = 0; i < getChildCount(); i++) { for (DynamicAnimation.ViewProperty property : properties) { - final DynamicAnimation anim = getAnimationAtIndex(property, i); + final DynamicAnimation anim = getSpringAnimationAtIndex(property, i); if (anim != null) { anim.cancel(); } } + final ViewPropertyAnimator anim = getViewPropertyAnimatorFromView(getChildAt(i)); + if (anim != null) { + anim.cancel(); + } } } @@ -441,7 +446,7 @@ public class PhysicsAnimationLayout extends FrameLayout { // Cancel physics animations on the view. for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) { - final DynamicAnimation animationFromView = getAnimationFromView(property, view); + final DynamicAnimation animationFromView = getSpringAnimationFromView(property, view); if (animationFromView != null) { animationFromView.cancel(); } @@ -502,17 +507,27 @@ public class PhysicsAnimationLayout extends FrameLayout { * Retrieves the animation of the given property from the view at the given index via the view * tag system. */ - @Nullable private SpringAnimation getAnimationAtIndex( + @Nullable private SpringAnimation getSpringAnimationAtIndex( DynamicAnimation.ViewProperty property, int index) { - return getAnimationFromView(property, getChildAt(index)); + return getSpringAnimationFromView(property, getChildAt(index)); } - /** Retrieves the animation of the given property from the view via the view tag system. */ - @Nullable private SpringAnimation getAnimationFromView( + /** + * Retrieves the spring animation of the given property from the view via the view tag system. + */ + @Nullable private SpringAnimation getSpringAnimationFromView( DynamicAnimation.ViewProperty property, View view) { return (SpringAnimation) view.getTag(getTagIdForProperty(property)); } + /** + * Retrieves the view property animation of the given property from the view via the view tag + * system. + */ + @Nullable private ViewPropertyAnimator getViewPropertyAnimatorFromView(View view) { + return (ViewPropertyAnimator) view.getTag(R.id.reorder_animator_tag); + } + /** Retrieves the target animator from the view via the view tag system. */ @Nullable private ObjectAnimator getTargetAnimatorFromView(View view) { return (ObjectAnimator) view.getTag(R.id.target_animator_tag); @@ -539,7 +554,8 @@ public class PhysicsAnimationLayout extends FrameLayout { final float offset = mController.getOffsetForChainedPropertyAnimation(property); if (nextAnimInChain < getChildCount()) { - final SpringAnimation nextAnim = getAnimationAtIndex(property, nextAnimInChain); + final SpringAnimation nextAnim = getSpringAnimationAtIndex( + property, nextAnimInChain); if (nextAnim != null) { nextAnim.animateToFinalPosition(value + offset); } @@ -902,9 +918,9 @@ public class PhysicsAnimationLayout extends FrameLayout { // and TRANSLATION_Y animations ending, and call them once both have finished. if (mPositionEndActions != null) { final SpringAnimation translationXAnim = - getAnimationFromView(DynamicAnimation.TRANSLATION_X, mView); + getSpringAnimationFromView(DynamicAnimation.TRANSLATION_X, mView); final SpringAnimation translationYAnim = - getAnimationFromView(DynamicAnimation.TRANSLATION_Y, mView); + getSpringAnimationFromView(DynamicAnimation.TRANSLATION_Y, mView); final Runnable waitForBothXAndY = () -> { if (!translationXAnim.isRunning() && !translationYAnim.isRunning()) { if (mPositionEndActions != null) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index c410b8267dd4..43893f215d8a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -24,6 +24,7 @@ import android.graphics.RectF; import android.provider.Settings; import android.util.Log; import android.view.View; +import android.view.ViewPropertyAnimator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -782,11 +783,11 @@ public class StackAnimationController extends } } - public void animateReorder(List<View> bubbleViews, Runnable after) { + public void animateReorder(List<View> bubbleViews, Runnable after) { for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) { View view = bubbleViews.get(newIndex); - final int oldIndex= mLayout.indexOfChild(view); - animateSwap(view, oldIndex, newIndex, after); + final int oldIndex = mLayout.indexOfChild(view); + animateSwap(view, oldIndex, newIndex, after); } } @@ -795,12 +796,15 @@ public class StackAnimationController extends final float swapY = newIndex == 0 ? newY - mSwapAnimationOffset // Above top of stack : newY + mSwapAnimationOffset; // Below where bubble will be - view.animate() + final ViewPropertyAnimator animator = view.animate() .scaleX(BUBBLE_SWAP_SCALE) .scaleY(BUBBLE_SWAP_SCALE) .translationY(swapY) .setDuration(BUBBLE_SWAP_DURATION) - .withEndAction(() -> finishSwapAnimation(view, oldIndex, newIndex, finishReorder)); + .withEndAction(() -> { + finishSwapAnimation(view, oldIndex, newIndex, finishReorder); + }); + view.setTag(R.id.reorder_animator_tag, animator); } private void finishSwapAnimation(View view, int oldIndex, int newIndex, @@ -818,12 +822,16 @@ public class StackAnimationController extends // Animate bubble back into stack, at new index and original size. final float newY = getStackPosition().y + newIndex * mStackOffset; - view.animate() + final ViewPropertyAnimator animator = view.animate() .scaleX(1f) .scaleY(1f) .translationY(newY) .setDuration(BUBBLE_SWAP_DURATION) - .withEndAction(() -> finishReorder.run()); + .withEndAction(() -> { + view.setTag(R.id.reorder_animator_tag, null); + finishReorder.run(); + }); + view.setTag(R.id.reorder_animator_tag, animator); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index 6b5f237ac76f..5a7e033607f8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -22,6 +22,8 @@ import android.content.pm.LauncherApps; import android.os.Handler; import android.view.WindowManager; +import androidx.annotation.Nullable; + import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.bubbles.BubbleController; @@ -41,10 +43,13 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.FloatingContentCoordinator; +import java.util.Optional; + import dagger.Module; import dagger.Provides; @@ -56,23 +61,8 @@ public interface BubbleModule { */ @SysUISingleton @Provides - static Bubbles newBubbleController( - Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, + static Bubbles newBubbleController(Context context, FloatingContentCoordinator floatingContentCoordinator, - SysUiState sysUiState, - INotificationManager notifManager, IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, @@ -80,30 +70,29 @@ public interface BubbleModule { UiEventLogger uiEventLogger, @Main Handler mainHandler, ShellTaskOrganizer organizer) { - return BubbleController.create( - context, - notificationShadeWindowController, - statusBarStateController, - shadeController, - null /* synchronizer */, - configurationController, - interruptionStateProvider, - zenModeController, - notifUserManager, - groupManager, - entryManager, - notifPipeline, - featureFlags, - dumpManager, - floatingContentCoordinator, - sysUiState, - notifManager, - statusBarService, - windowManager, - windowManagerShellWrapper, - launcherApps, - uiEventLogger, - mainHandler, - organizer); + return BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator, + statusBarService, windowManager, windowManagerShellWrapper, launcherApps, + uiEventLogger, mainHandler, organizer); + } + + /** Provides Optional of BubbleManager */ + @SysUISingleton + @Provides + static Optional<BubblesManager> provideBubblesManager(Context context, + Optional<Bubbles> bubblesOptional, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, ShadeController shadeController, + ConfigurationController configurationController, + @Nullable IStatusBarService statusBarService, INotificationManager notificationManager, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, + NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags, + DumpManager dumpManager) { + return Optional.ofNullable(BubblesManager.create(context, bubblesOptional, + notificationShadeWindowController, statusBarStateController, shadeController, + configurationController, statusBarService, notificationManager, + interruptionStateProvider, zenModeController, notifUserManager, + groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager)); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 80928d6da978..451bd42bd053 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -16,10 +16,12 @@ package com.android.systemui.media.dialog; +import android.app.Notification; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.MediaMetadata; import android.media.MediaRoute2Info; import android.media.RoutingSessionInfo; @@ -221,9 +223,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { } for (NotificationEntry entry : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { - if (entry.getSbn().getNotification().hasMediaSession() + final Notification notification = entry.getSbn().getNotification(); + if (notification.hasMediaSession() && TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) { - return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon()); + final Icon icon = notification.getLargeIcon(); + if (icon == null) { + break; + } + return IconCompat.createFromIcon(icon); } } return null; diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt index 81076475c5ce..6ac1e7079531 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt @@ -99,7 +99,7 @@ class DoubleLineTileLayout( } } - override fun getNumVisibleTiles() = tilesToShow + override fun getNumVisibleTiles() = Math.min(mRecords.size, tilesToShow) override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 1b17a2a277f2..76f244652cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -480,7 +480,6 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi } } mTileLayout = newLayout; - newLayout.setListening(mListening); if (needsDynamicRowsAndColumns()) { newLayout.setMinRows(horizontal ? 2 : 1); // Let's use 3 columns to match the current layout @@ -498,6 +497,14 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi return false; } + /** + * Sets the listening state of the current layout to the state of the view. Used after + * switching layouts. + */ + public void reSetLayoutListening() { + mTileLayout.setListening(mListening); + } + private void updateHorizontalLinearLayoutMargins() { if (mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) { LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index fe92827806c6..68a6cdcbd289 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -214,6 +214,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr boolean switchTileLayout(boolean force) { if (mView.switchTileLayout(force, mRecords)) { setTiles(); + mView.reSetLayoutListening(); return true; } return false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 84a5b6f0538d..ed0900d07b56 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -336,7 +336,7 @@ public class QuickQSPanel extends QSPanel { @Override public int getNumVisibleTiles() { - return mColumns; + return Math.min(mRecords.size(), mColumns); } @Override @@ -353,6 +353,7 @@ public class QuickQSPanel extends QSPanel { boolean startedListening = !mListening && listening; super.setListening(listening); if (startedListening) { + // getNumVisibleTiles() <= mRecords.size() for (int i = 0; i < getNumVisibleTiles(); i++) { QSTile tile = mRecords.get(i).tile; mUiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index f063fbe7a384..260f55799e0b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -38,6 +38,7 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.media.MediaActionSound; import android.net.Uri; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -143,6 +144,7 @@ public class ScreenshotController { private final AccessibilityManager mAccessibilityManager; private final MediaActionSound mCameraSound; + private final Binder mWindowToken; private ScreenshotView mScreenshotView; private Bitmap mScreenBitmap; private SaveImageInBackgroundTask mSaveInBgTask; @@ -178,13 +180,9 @@ public class ScreenshotController { mNotificationsController = screenshotNotificationsController; mUiEventLogger = uiEventLogger; - // Create a visual (Window) context - // After this, our windowToken is available from mContext.getActivityToken() final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class)); mDisplay = dm.getDisplay(DEFAULT_DISPLAY); - Context displayContext = context.createDisplayContext(mDisplay); - mContext = new WindowContext(displayContext, WindowManager.LayoutParams.TYPE_SCREENSHOT, - null); + mContext = context.createDisplayContext(mDisplay); mWindowManager = mContext.getSystemService(WindowManager.class); mAccessibilityManager = AccessibilityManager.getInstance(mContext); @@ -194,6 +192,7 @@ public class ScreenshotController { mInDarkMode = config.isNightModeActive(); mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT; + mWindowToken = new Binder("ScreenshotController"); // Setup the window that we are going to use mWindowLayoutParams = new WindowManager.LayoutParams( @@ -210,6 +209,7 @@ public class ScreenshotController { mWindowLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mWindowLayoutParams.setFitInsetsTypes(0 /* types */); + mWindowLayoutParams.token = mWindowToken; mDisplayMetrics = new DisplayMetrics(); mDisplay.getRealMetrics(mDisplayMetrics); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 53179ba4be90..a92b9e4818b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -159,7 +159,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent() - && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(ent); + && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade( + ent.getKey(), ent.getSbn().getGroupKey()); if (ent.isRowDismissed() || ent.isRowRemoved() || isBubbleNotificationSuppressedFromShade || mFgsSectionController.hasEntry(ent)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index ddfa18e65ee0..44b9bd26aa38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -46,7 +46,7 @@ class ConversationNotificationProcessor @Inject constructor( Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT else Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL - entry.ranking.shortcutInfo?.let { shortcutInfo -> + entry.ranking.conversationShortcutInfo?.let { shortcutInfo -> messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo) shortcutInfo.label?.let { label -> messagingStyle.conversationTitle = label diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java index c301002ef8d0..65e333f14df6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java @@ -47,7 +47,7 @@ public class NotificationChannelHelper { final String pkg = entry.getSbn().getPackageName(); final int appUid = entry.getSbn().getUid(); if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg) - || entry.getRanking().getShortcutInfo() == null) { + || entry.getRanking().getConversationShortcutInfo() == null) { return channel; } @@ -68,8 +68,8 @@ public class NotificationChannelHelper { } private static CharSequence getName(NotificationEntry entry) { - if (entry.getRanking().getShortcutInfo().getLabel() != null) { - return entry.getRanking().getShortcutInfo().getLabel().toString(); + if (entry.getRanking().getConversationShortcutInfo().getLabel() != null) { + return entry.getRanking().getConversationShortcutInfo().getLabel().toString(); } Bundle extras = entry.getSbn().getNotification().extras; CharSequence nameString = extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java index 0455b0f18afc..83a569bd6573 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java @@ -25,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; +import com.android.systemui.wmshell.BubblesManager; import java.util.HashSet; import java.util.Optional; @@ -56,6 +57,7 @@ import javax.inject.Inject; public class BubbleCoordinator implements Coordinator { private static final String TAG = "BubbleCoordinator"; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Optional<Bubbles> mBubblesOptional; private final NotifCollection mNotifCollection; private final Set<String> mInterceptedDismissalEntries = new HashSet<>(); @@ -64,8 +66,10 @@ public class BubbleCoordinator implements Coordinator { @Inject public BubbleCoordinator( + Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, NotifCollection notifCollection) { + mBubblesManagerOptional = bubblesManagerOptional; mBubblesOptional = bubblesOptional; mNotifCollection = notifCollection; } @@ -75,8 +79,8 @@ public class BubbleCoordinator implements Coordinator { mNotifPipeline = pipeline; mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor); mNotifPipeline.addFinalizeFilter(mNotifFilter); - if (mBubblesOptional.isPresent()) { - mBubblesOptional.get().addNotifCallback(mNotifCallback); + if (mBubblesManagerOptional.isPresent()) { + mBubblesManagerOptional.get().addNotifCallback(mNotifCallback); } } @@ -85,7 +89,8 @@ public class BubbleCoordinator implements Coordinator { @Override public boolean shouldFilterOut(NotificationEntry entry, long now) { return mBubblesOptional.isPresent() - && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(entry); + && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getSbn().getGroupKey()); } }; @@ -102,9 +107,8 @@ public class BubbleCoordinator implements Coordinator { @Override public boolean shouldInterceptDismissal(NotificationEntry entry) { - // for experimental bubbles - if (mBubblesOptional.isPresent() - && mBubblesOptional.get().handleDismissalInterception(entry)) { + if (mBubblesManagerOptional.isPresent() + && mBubblesManagerOptional.get().handleDismissalInterception(entry)) { mInterceptedDismissalEntries.add(entry.getKey()); return true; } else { @@ -119,8 +123,7 @@ public class BubbleCoordinator implements Coordinator { } }; - private final BubbleController.NotifCallback mNotifCallback = - new BubbleController.NotifCallback() { + private final BubblesManager.NotifCallback mNotifCallback = new BubblesManager.NotifCallback() { @Override public void removeNotification( NotificationEntry entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java index 490989dbb39e..36adfac21bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java @@ -64,7 +64,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, new ArraySet<>(); private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>(); private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier; - private final Optional<Lazy<Bubbles>> mBubblesOptional; + private final Optional<Bubbles> mBubblesOptional; private int mBarState = -1; private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>(); private HeadsUpManager mHeadsUpManager; @@ -74,7 +74,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, public NotificationGroupManagerLegacy( StatusBarStateController statusBarStateController, Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier, - Optional<Lazy<Bubbles>> bubblesOptional) { + Optional<Bubbles> bubblesOptional) { statusBarStateController.addCallback(this); mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesOptional = bubblesOptional; @@ -242,8 +242,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, int childCount = 0; boolean hasBubbles = false; for (NotificationEntry entry : group.children.values()) { - if (mBubblesOptional.isPresent() && !mBubblesOptional.get().get() - .isBubbleNotificationSuppressedFromShade(entry)) { + if (mBubblesOptional.isPresent() && !mBubblesOptional.get() + .isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getSbn().getGroupKey())) { childCount++; } else { hasBubbles = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 4fff99b482d8..ff55cd60ab3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -26,7 +26,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -73,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogC import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; +import com.android.systemui.wmshell.BubblesManager; import java.util.Optional; import java.util.concurrent.Executor; @@ -133,7 +133,7 @@ public interface NotificationsModule { UserContextProvider contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManagerOptional, UiEventLogger uiEventLogger, OnUserInteractionCallback onUserInteractionCallback) { return new NotificationGutsManager( @@ -150,7 +150,7 @@ public interface NotificationsModule { contextTracker, builderProvider, assistantFeedbackController, - bubblesOptional, + bubblesManagerOptional, uiEventLogger, onUserInteractionCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt index 13f7a53f5e54..ba45f9a687ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt @@ -255,7 +255,7 @@ class IconManager @Inject constructor( private fun createPeopleAvatar(entry: NotificationEntry): Icon? { var ic: Icon? = null - val shortcut = entry.ranking.shortcutInfo + val shortcut = entry.ranking.conversationShortcutInfo if (shortcut != null) { ic = launcherApps.getShortcutIcon(shortcut) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt index 99b2fcc9d610..cd9ba4e690e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt @@ -220,7 +220,7 @@ class PeopleHubDataSourceImpl @Inject constructor( } val clickRunnable = Runnable { notificationListener.unsnoozeNotification(key) } val extras = sbn.notification.extras - val name = ranking.shortcutInfo?.label + val name = ranking.conversationShortcutInfo?.label ?: extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE) ?: extras.getCharSequence(Notification.EXTRA_TITLE) ?: return null @@ -238,9 +238,9 @@ class PeopleHubDataSourceImpl @Inject constructor( iconFactory: ConversationIconFactory, sbn: StatusBarNotification ): Drawable? = - shortcutInfo?.let { shortcutInfo -> + conversationShortcutInfo?.let { conversationShortcutInfo -> iconFactory.getConversationDrawable( - shortcutInfo, + conversationShortcutInfo, sbn.packageName, sbn.uid, channel.isImportantConversation diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index 0d92616767f3..691f1f452da8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -103,7 +103,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor( private val Ranking.personTypeInfo get() = when { !isConversation -> TYPE_NON_PERSON - shortcutInfo == null -> TYPE_PERSON + conversationShortcutInfo == null -> TYPE_PERSON channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON else -> TYPE_FULL_PERSON } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5ee66fabe55a..280c525f41cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -72,7 +72,6 @@ import com.android.internal.widget.CachingIconView; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -102,12 +101,14 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; +import com.android.systemui.wmshell.BubblesManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -147,6 +148,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private LayoutListener mLayoutListener; private RowContentBindStage mRowContentBindStage; private PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private Optional<BubblesManager> mBubblesManagerOptional; private int mIconTransformContentShift; private int mMaxHeadsUpHeightBeforeN; private int mMaxHeadsUpHeightBeforeP; @@ -1078,13 +1080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** The click listener for the bubble button. */ public View.OnClickListener getBubbleClickListener() { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - Dependency.get(Bubbles.class) - .onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */); - mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */); + return v -> { + if (mBubblesManagerOptional.isPresent()) { + mBubblesManagerOptional.get() + .onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */); } + mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */); }; } @@ -1553,7 +1554,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView FalsingManager falsingManager, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier, - OnUserInteractionCallback onUserInteractionCallback) { + OnUserInteractionCallback onUserInteractionCallback, + Optional<BubblesManager> bubblesManagerOptional) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { @@ -1581,6 +1583,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier); } mOnUserInteractionCallback = onUserInteractionCallback; + mBubblesManagerOptional = bubblesManagerOptional; cacheIsSystemNotification(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index c995e324ecfe..05b1dba36a7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -43,8 +43,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.time.SystemClock; +import com.android.systemui.wmshell.BubblesManager; import java.util.List; +import java.util.Optional; import javax.inject.Inject; import javax.inject.Named; @@ -79,6 +81,7 @@ public class ExpandableNotificationRowController implements NodeController { private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private final Optional<BubblesManager> mBubblesManagerOptional; @Inject public ExpandableNotificationRowController( @@ -102,7 +105,8 @@ public class ExpandableNotificationRowController implements NodeController { @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + Optional<BubblesManager> bubblesManagerOptional) { mView = view; mListContainer = listContainer; mActivatableNotificationViewController = activatableNotificationViewController; @@ -125,6 +129,7 @@ public class ExpandableNotificationRowController implements NodeController { mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; + mBubblesManagerOptional = bubblesManagerOptional; } /** @@ -148,8 +153,8 @@ public class ExpandableNotificationRowController implements NodeController { mFalsingManager, mStatusBarStateController, mPeopleNotificationIdentifier, - mOnUserInteractionCallback - + mOnUserInteractionCallback, + mBubblesManagerOptional ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 07a4a188bc48..e43130f1698b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -67,12 +67,12 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.wmshell.BubblesManager; import java.lang.annotation.Retention; import java.util.Optional; @@ -94,7 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements private OnUserInteractionCallback mOnUserInteractionCallback; private Handler mMainHandler; private Handler mBgHandler; - private Optional<Bubbles> mBubblesOptional; + private Optional<BubblesManager> mBubblesManagerOptional; private String mPackageName; private String mAppName; private int mAppUid; @@ -223,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements @Main Handler mainHandler, @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, - Optional<Bubbles> bubblesOptional) { + Optional<BubblesManager> bubblesManagerOptional) { mSelectedAction = -1; mINotificationManager = iNotificationManager; mOnUserInteractionCallback = onUserInteractionCallback; @@ -242,12 +242,12 @@ public class NotificationConversationInfo extends LinearLayout implements mIconFactory = conversationIconFactory; mUserContext = userContext; mBubbleMetadata = bubbleMetadata; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManagerOptional; mBuilderProvider = builderProvider; mMainHandler = mainHandler; mBgHandler = bgHandler; mShortcutManager = shortcutManager; - mShortcutInfo = entry.getRanking().getShortcutInfo(); + mShortcutInfo = entry.getRanking().getConversationShortcutInfo(); if (mShortcutInfo == null) { throw new IllegalArgumentException("Does not have required information"); } @@ -641,9 +641,9 @@ public class NotificationConversationInfo extends LinearLayout implements mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid, BUBBLE_PREFERENCE_SELECTED); } - if (mBubblesOptional.isPresent()) { + if (mBubblesManagerOptional.isPresent()) { post(() -> { - mBubblesOptional.get().onUserChangedImportance(mEntry); + mBubblesManagerOptional.get().onUserChangedImportance(mEntry); }); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 373f20e6ba96..d2cfb2908e9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -47,7 +47,6 @@ import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -67,6 +66,7 @@ import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSav import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.wmshell.BubblesManager; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -117,7 +117,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final Lazy<StatusBar> mStatusBarLazy; private final Handler mMainHandler; private final Handler mBgHandler; - private final Optional<Bubbles> mBubblesOptional; + private final Optional<BubblesManager> mBubblesManagerOptional; private Runnable mOpenRunnable; private final INotificationManager mNotificationManager; private final LauncherApps mLauncherApps; @@ -142,7 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx UserContextProvider contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManagerOptional, UiEventLogger uiEventLogger, OnUserInteractionCallback onUserInteractionCallback) { mContext = context; @@ -158,7 +158,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mBuilderProvider = builderProvider; mChannelEditorDialogController = channelEditorDialogController; mAssistantFeedbackController = assistantFeedbackController; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManagerOptional; mUiEventLogger = uiEventLogger; mOnUserInteractionCallback = onUserInteractionCallback; } @@ -491,7 +491,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mMainHandler, mBgHandler, onConversationSettingsListener, - mBubblesOptional); + mBubblesManagerOptional); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index e065b47ef5b1..f2ae3da73f5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -306,7 +306,8 @@ public class NotificationIconAreaController implements || !entry.isPulseSuppressed())) { return false; } - if (mBubblesOptional.isPresent() && mBubblesOptional.get().isBubbleExpanded(entry)) { + if (mBubblesOptional.isPresent() + && mBubblesOptional.get().isBubbleExpanded(entry.getKey())) { return false; } return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 9bd98f2e8c24..a8d41046a1eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -144,7 +144,6 @@ import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.Bubbles; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingLog; @@ -229,6 +228,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; @@ -648,8 +648,9 @@ public class StatusBar extends SystemUI implements DemoMode, protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Optional<Bubbles> mBubblesOptional; - private final BubbleController.BubbleExpandListener mBubbleExpandListener; + private final Bubbles.BubbleExpandListener mBubbleExpandListener; private ActivityIntentHelper mActivityIntentHelper; private NotificationStackScrollLayoutController mStackScrollerController; @@ -697,6 +698,7 @@ public class StatusBar extends SystemUI implements DemoMode, WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, + Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, @@ -776,6 +778,7 @@ public class StatusBar extends SystemUI implements DemoMode, mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mVibratorHelper = vibratorHelper; + mBubblesManagerOptional = bubblesManagerOptional; mBubblesOptional = bubblesOptional; mVisualStabilityManager = visualStabilityManager; mDeviceProvisionedController = deviceProvisionedController; @@ -1141,8 +1144,8 @@ public class StatusBar extends SystemUI implements DemoMode, ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind); ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front); - ScrimView scrimForBubble = mBubblesOptional.isPresent() - ? mBubblesOptional.get().getScrimForBubble() : null; + ScrimView scrimForBubble = mBubblesManagerOptional.isPresent() + ? mBubblesManagerOptional.get().getScrimForBubble() : null; mScrimController.setScrimVisibleListener(scrimsVisible -> { mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 014d5c274c1f..acca953629c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -48,7 +48,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.assist.AssistManager; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -76,6 +75,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.wmshell.BubblesManager; import java.util.Optional; import java.util.concurrent.Executor; @@ -104,7 +104,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; - private final Optional<Bubbles> mBubblesOptional; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; private final GroupMembershipManager mGroupMembershipManager; @@ -142,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManagerOptional, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, GroupMembershipManager groupMembershipManager, @@ -176,7 +176,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManagerOptional; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; mGroupMembershipManager = groupMembershipManager; @@ -399,14 +399,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void expandBubbleStackOnMainThread(NotificationEntry entry) { - if (!mBubblesOptional.isPresent()) { + if (!mBubblesManagerOptional.isPresent()) { return; } if (Looper.getMainLooper().isCurrentThread()) { - mBubblesOptional.get().expandStackAndSelectBubble(entry); + mBubblesManagerOptional.get().expandStackAndSelectBubble(entry); } else { - mMainThreadHandler.post(() -> mBubblesOptional.get().expandStackAndSelectBubble(entry)); + mMainThreadHandler.post( + () -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry)); } } @@ -606,7 +607,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; - private final Optional<Bubbles> mBubblesOptional; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; private final GroupMembershipManager mGroupMembershipManager; @@ -643,7 +644,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManager, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, GroupMembershipManager groupMembershipManager, @@ -673,7 +674,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManager; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; mGroupMembershipManager = groupMembershipManager; @@ -729,7 +730,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager, mKeyguardManager, mDreamManager, - mBubblesOptional, + mBubblesManagerOptional, mAssistManagerLazy, mRemoteInputManager, mGroupMembershipManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 6d4099b656cb..b69da859b3c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -97,6 +97,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; @@ -155,6 +156,7 @@ public interface StatusBarPhoneModule { WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, + Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, @@ -233,6 +235,7 @@ public interface StatusBarPhoneModule { wakefulnessLifecycle, statusBarStateController, vibratorHelper, + bubblesManagerOptional, bubblesOptional, visualStabilityManager, deviceProvisionedController, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java new file mode 100644 index 000000000000..ad596c27ba97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -0,0 +1,725 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wmshell; + +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; +import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_CLICK; +import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; +import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; + +import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; +import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; + +import android.app.INotificationManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.res.Configuration; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.notification.NotificationListenerService.RankingMap; +import android.service.notification.ZenModeConfig; +import android.util.ArraySet; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.Dumpable; +import com.android.systemui.bubbles.BubbleEntry; +import com.android.systemui.bubbles.Bubbles; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.statusbar.notification.NotificationChannelHelper; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ZenModeController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.IntConsumer; + +/** + * The SysUi side bubbles manager which communicate with other SysUi components. + */ +@SysUISingleton +public class BubblesManager implements Dumpable { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesManager" : TAG_BUBBLES; + + private final Context mContext; + private final Bubbles mBubbles; + private final NotificationShadeWindowController mNotificationShadeWindowController; + private final ShadeController mShadeController; + private final IStatusBarService mBarService; + private final INotificationManager mNotificationManager; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationGroupManagerLegacy mNotificationGroupManager; + private final NotificationEntryManager mNotificationEntryManager; + private final NotifPipeline mNotifPipeline; + + private final ScrimView mBubbleScrim; + private final Bubbles.SysuiProxy mSysuiProxy; + // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline + private final List<NotifCallback> mCallbacks = new ArrayList<>(); + + /** + * Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present + * which means bubbles feature not support. + */ + @Nullable + public static BubblesManager create(Context context, + Optional<Bubbles> bubblesOptional, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, + ShadeController shadeController, + ConfigurationController configurationController, + @Nullable IStatusBarService statusBarService, + INotificationManager notificationManager, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, + NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, + NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + SysUiState sysUiState, + FeatureFlags featureFlags, + DumpManager dumpManager) { + if (bubblesOptional.isPresent()) { + return new BubblesManager(context, bubblesOptional.get(), + notificationShadeWindowController, statusBarStateController, shadeController, + configurationController, statusBarService, notificationManager, + interruptionStateProvider, zenModeController, notifUserManager, + groupManager, entryManager, notifPipeline, sysUiState, featureFlags, + dumpManager); + } else { + return null; + } + } + + @VisibleForTesting + BubblesManager(Context context, + Bubbles bubbles, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, + ShadeController shadeController, + ConfigurationController configurationController, + @Nullable IStatusBarService statusBarService, + INotificationManager notificationManager, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, + NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, + NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + SysUiState sysUiState, + FeatureFlags featureFlags, + DumpManager dumpManager) { + mContext = context; + mBubbles = bubbles; + mNotificationShadeWindowController = notificationShadeWindowController; + mShadeController = shadeController; + mNotificationManager = notificationManager; + mNotificationInterruptStateProvider = interruptionStateProvider; + mNotificationGroupManager = groupManager; + mNotificationEntryManager = entryManager; + mNotifPipeline = notifPipeline; + + mBarService = statusBarService == null + ? IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)) + : statusBarService; + + mBubbleScrim = new ScrimView(mContext); + mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + mBubbles.setBubbleScrim(mBubbleScrim); + + if (featureFlags.isNewNotifPipelineRenderingEnabled()) { + setupNotifPipeline(); + } else { + setupNEM(); + } + + dumpManager.registerDumpable(TAG, this); + + statusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + boolean isShade = newState == SHADE; + bubbles.onStatusBarStateChanged(isShade); + } + }); + + configurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onConfigChanged(Configuration newConfig) { + mBubbles.onConfigChanged(newConfig); + } + + @Override + public void onUiModeChanged() { + mBubbles.updateForThemeChanges(); + } + + @Override + public void onThemeChanged() { + mBubbles.updateForThemeChanges(); + } + }); + + zenModeController.addCallback(new ZenModeController.Callback() { + @Override + public void onZenChanged(int zen) { + mBubbles.onZenStateChanged(); + } + + @Override + public void onConfigChanged(ZenModeConfig config) { + mBubbles.onZenStateChanged(); + } + }); + + notifUserManager.addUserChangedListener( + new NotificationLockscreenUserManager.UserChangedListener() { + @Override + public void onUserChanged(int userId) { + mBubbles.onUserChanged(userId); + } + }); + + mSysuiProxy = new Bubbles.SysuiProxy() { + @Override + @Nullable + public BubbleEntry getPendingOrActiveEntry(String key) { + NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); + return entry == null ? null : notifToBubbleEntry(entry); + } + + @Override + public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) { + List<BubbleEntry> result = new ArrayList<>(); + List<NotificationEntry> activeEntries = + mNotificationEntryManager.getActiveNotificationsForCurrentUser(); + for (int i = 0; i < activeEntries.size(); i++) { + NotificationEntry entry = activeEntries.get(i); + if (savedBubbleKeys.contains(entry.getKey()) + && mNotificationInterruptStateProvider.shouldBubbleUp(entry) + && entry.isBubble()) { + result.add(notifToBubbleEntry(entry)); + } + } + return result; + } + + @Override + public boolean isNotificationShadeExpand() { + return mNotificationShadeWindowController.getPanelExpanded(); + } + + @Override + public boolean shouldBubbleUp(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + return mNotificationInterruptStateProvider.shouldBubbleUp(entry); + } + return false; + } + + @Override + public void setNotificationInterruption(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { + entry.setInterruption(); + } + } + + @Override + public void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag) { + mNotificationShadeWindowController.setRequestTopUi(requestTopUi, componentTag); + } + + @Override + public void notifyRemoveNotification(String key, int reason) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + for (NotifCallback cb : mCallbacks) { + cb.removeNotification(entry, getDismissedByUserStats(entry, true), reason); + } + } + } + + @Override + public void notifyInvalidateNotifications(String reason) { + for (NotifCallback cb : mCallbacks) { + cb.invalidateNotifications(reason); + } + } + + @Override + public void notifyMaybeCancelSummary(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + for (NotifCallback cb : mCallbacks) { + cb.maybeCancelSummary(entry); + } + } + } + + @Override + public void removeNotificationEntry(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + mNotificationGroupManager.onEntryRemoved(entry); + } + } + + @Override + public void updateNotificationBubbleButton(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null && entry.getRow() != null) { + entry.getRow().updateBubbleButton(); + } + } + + @Override + public void updateNotificationSuppression(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + mNotificationGroupManager.updateSuppression(entry); + } + } + + @Override + public void onStackExpandChanged(boolean shouldExpand) { + sysUiState + .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand) + .commitUpdate(mContext.getDisplayId()); + } + + @Override + public void onUnbubbleConversation(String key) { + final NotificationEntry entry = + mNotificationEntryManager.getPendingOrActiveNotif(key); + if (entry != null) { + onUserChangedBubble(entry, false /* shouldBubble */); + } + } + }; + mBubbles.setSysuiProxy(mSysuiProxy); + } + + private void setupNEM() { + mNotificationEntryManager.addNotificationEntryListener( + new NotificationEntryListener() { + @Override + public void onPendingEntryAdded(NotificationEntry entry) { + BubblesManager.this.onEntryAdded(entry); + } + + @Override + public void onPreEntryUpdated(NotificationEntry entry) { + BubblesManager.this.onEntryUpdated(entry); + } + + @Override + public void onEntryRemoved(NotificationEntry entry, + @Nullable NotificationVisibility visibility, + boolean removedByUser, int reason) { + BubblesManager.this.onEntryRemoved(entry); + } + + @Override + public void onNotificationRankingUpdated(RankingMap rankingMap) { + BubblesManager.this.onRankingUpdate(rankingMap); + } + }); + + // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator + mNotificationEntryManager.addNotificationRemoveInterceptor( + (key, entry, dismissReason) -> { + final boolean isClearAll = dismissReason == REASON_CANCEL_ALL; + final boolean isUserDismiss = dismissReason == REASON_CANCEL + || dismissReason == REASON_CLICK; + final boolean isAppCancel = dismissReason == REASON_APP_CANCEL + || dismissReason == REASON_APP_CANCEL_ALL; + final boolean isSummaryCancel = + dismissReason == REASON_GROUP_SUMMARY_CANCELED; + + // Need to check for !appCancel here because the notification may have + // previously been dismissed & entry.isRowDismissed would still be true + boolean userRemovedNotif = + (entry != null && entry.isRowDismissed() && !isAppCancel) + || isClearAll || isUserDismiss || isSummaryCancel; + + if (userRemovedNotif) { + return handleDismissalInterception(entry); + } + return false; + }); + + mNotificationGroupManager.registerGroupChangeListener( + new NotificationGroupManagerLegacy.OnGroupChangeListener() { + @Override + public void onGroupSuppressionChanged( + NotificationGroupManagerLegacy.NotificationGroup group, + boolean suppressed) { + // More notifications could be added causing summary to no longer + // be suppressed -- in this case need to remove the key. + final String groupKey = group.summary != null + ? group.summary.getSbn().getGroupKey() + : null; + if (!suppressed && groupKey != null + && mBubbles.isSummarySuppressed(groupKey)) { + mBubbles.removeSuppressedSummary(groupKey); + } + } + }); + + addNotifCallback(new NotifCallback() { + @Override + public void removeNotification(NotificationEntry entry, + DismissedByUserStats dismissedByUserStats, int reason) { + mNotificationEntryManager.performRemoveNotification(entry.getSbn(), + dismissedByUserStats, reason); + } + + @Override + public void invalidateNotifications(String reason) { + mNotificationEntryManager.updateNotifications(reason); + } + + @Override + public void maybeCancelSummary(NotificationEntry entry) { + // Check if removed bubble has an associated suppressed group summary that needs + // to be removed now. + final String groupKey = entry.getSbn().getGroupKey(); + if (mBubbles.isSummarySuppressed(groupKey)) { + mBubbles.removeSuppressedSummary(groupKey); + + final NotificationEntry summary = + mNotificationEntryManager.getActiveNotificationUnfiltered( + mBubbles.getSummaryKey(groupKey)); + if (summary != null) { + mNotificationEntryManager.performRemoveNotification( + summary.getSbn(), + getDismissedByUserStats(summary, false), + UNDEFINED_DISMISS_REASON); + } + } + + // Check if we still need to remove the summary from NoManGroup because the summary + // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above. + // For example: + // 1. Bubbled notifications (group) is posted to shade and are visible bubbles + // 2. User expands bubbles so now their respective notifications in the shade are + // hidden, including the group summary + // 3. User removes all bubbles + // 4. We expect all the removed bubbles AND the summary (note: the summary was + // never added to the suppressedSummary list in BubbleData, so we add this check) + NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry); + if (summary != null) { + ArrayList<NotificationEntry> summaryChildren = + mNotificationGroupManager.getLogicalChildren(summary.getSbn()); + boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey()); + if (!isSummaryThisNotif && (summaryChildren == null + || summaryChildren.isEmpty())) { + mNotificationEntryManager.performRemoveNotification( + summary.getSbn(), + getDismissedByUserStats(summary, false), + UNDEFINED_DISMISS_REASON); + } + } + } + }); + } + + private void setupNotifPipeline() { + mNotifPipeline.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryAdded(NotificationEntry entry) { + BubblesManager.this.onEntryAdded(entry); + } + + @Override + public void onEntryUpdated(NotificationEntry entry) { + BubblesManager.this.onEntryUpdated(entry); + } + + @Override + public void onEntryRemoved(NotificationEntry entry, + @NotifCollection.CancellationReason int reason) { + BubblesManager.this.onEntryRemoved(entry); + } + + @Override + public void onRankingUpdate(RankingMap rankingMap) { + BubblesManager.this.onRankingUpdate(rankingMap); + } + }); + } + + void onEntryAdded(NotificationEntry entry) { + if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) + && entry.isBubble()) { + mBubbles.onEntryAdded(notifToBubbleEntry(entry)); + } + } + + void onEntryUpdated(NotificationEntry entry) { + mBubbles.onEntryUpdated(notifToBubbleEntry(entry), + mNotificationInterruptStateProvider.shouldBubbleUp(entry)); + } + + void onEntryRemoved(NotificationEntry entry) { + mBubbles.onEntryRemoved(notifToBubbleEntry(entry)); + } + + void onRankingUpdate(RankingMap rankingMap) { + mBubbles.onRankingUpdated(rankingMap); + } + + /** + * Gets the DismissedByUserStats used by {@link NotificationEntryManager}. + * Will not be necessary when using the new notification pipeline's {@link NotifCollection}. + * Instead, this is taken care of by {@link BubbleCoordinator}. + */ + private DismissedByUserStats getDismissedByUserStats( + NotificationEntry entry, + boolean isVisible) { + return new DismissedByUserStats( + DISMISSAL_BUBBLE, + DISMISS_SENTIMENT_NEUTRAL, + NotificationVisibility.obtain( + entry.getKey(), + entry.getRanking().getRank(), + mNotificationEntryManager.getActiveNotificationsCount(), + isVisible, + NotificationLogger.getNotificationLocation(entry))); + } + + /** + * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController} + * since we want the scrim's appearance and behavior to be identical to that of the notification + * shade scrim. + */ + public ScrimView getScrimForBubble() { + return mBubbleScrim; + } + + /** + * We intercept notification entries (including group summaries) dismissed by the user when + * there is an active bubble associated with it. We do this so that developers can still + * cancel it (and hence the bubbles associated with it). + * + * @return true if we want to intercept the dismissal of the entry, else false. + * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer) + */ + public boolean handleDismissalInterception(NotificationEntry entry) { + if (entry == null) { + return false; + } + + List<NotificationEntry> children = entry.getAttachedNotifChildren(); + List<BubbleEntry> bubbleChildren = null; + if (children != null) { + bubbleChildren = new ArrayList<>(); + for (int i = 0; i < children.size(); i++) { + bubbleChildren.add(notifToBubbleEntry(children.get(i))); + } + } + + return mBubbles.handleDismissalInterception(notifToBubbleEntry(entry), bubbleChildren, + // TODO : b/171847985 should re-work on notification side to make this more clear. + (int i) -> { + if (i >= 0) { + for (NotifCallback cb : mCallbacks) { + cb.removeNotification(children.get(i), + getDismissedByUserStats(children.get(i), true), + REASON_GROUP_SUMMARY_CANCELED); + } + } else { + mNotificationGroupManager.onEntryRemoved(entry); + } + }); + } + + /** + * Request the stack expand if needed, then select the specified Bubble as current. + * If no bubble exists for this entry, one is created. + * + * @param entry the notification for the bubble to be selected + */ + public void expandStackAndSelectBubble(NotificationEntry entry) { + mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry)); + } + + /** See {@link NotifCallback}. */ + public void addNotifCallback(NotifCallback callback) { + mCallbacks.add(callback); + } + + /** + * When a notification is marked Priority, expand the stack if needed, + * then (maybe create and) select the given bubble. + * + * @param entry the notification for the bubble to show + */ + public void onUserChangedImportance(NotificationEntry entry) { + try { + int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; + mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + mShadeController.collapsePanel(true); + if (entry.getRow() != null) { + entry.getRow().updateBubbleButton(); + } + } + + /** + * Called when a user has indicated that an active notification should be shown as a bubble. + * <p> + * This method will collapse the shade, create the bubble without a flyout or dot, and suppress + * the notification from appearing in the shade. + * + * @param entry the notification to change bubble state for. + * @param shouldBubble whether the notification should show as a bubble or not. + */ + public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) { + NotificationChannel channel = entry.getChannel(); + final String appPkg = entry.getSbn().getPackageName(); + final int appUid = entry.getSbn().getUid(); + if (channel == null || appPkg == null) { + return; + } + + // Update the state in NotificationManagerService + try { + int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; + mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags); + } catch (RemoteException e) { + } + + // Change the settings + channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext, + mNotificationManager, entry, channel); + channel.setAllowBubbles(shouldBubble); + try { + int currentPref = mNotificationManager.getBubblePreferenceForPackage(appPkg, appUid); + if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) { + mNotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED); + } + mNotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + + if (shouldBubble) { + mShadeController.collapsePanel(true); + if (entry.getRow() != null) { + entry.getRow().updateBubbleButton(); + } + } + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + mBubbles.dump(fd, pw, args); + } + + static BubbleEntry notifToBubbleEntry(NotificationEntry e) { + return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), + e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), + e.shouldSuppressPeek()); + } + + /** + * Callback for when the BubbleController wants to interact with the notification pipeline to: + * - Remove a previously bubbled notification + * - Update the notification shade since bubbled notification should/shouldn't be showing + */ + public interface NotifCallback { + /** + * Called when a bubbled notification that was hidden from the shade is now being removed + * This can happen when an app cancels a bubbled notification or when the user dismisses a + * bubble. + */ + void removeNotification(@NonNull NotificationEntry entry, + @NonNull DismissedByUserStats stats, int reason); + + /** + * Called when a bubbled notification has changed whether it should be + * filtered from the shade. + */ + void invalidateNotifications(@NonNull String reason); + + /** + * Called on a bubbled entry that has been removed when there are no longer + * bubbled entries in its group. + * + * Checks whether its group has any other (non-bubbled) children. If it doesn't, + * removes all remnants of the group's summary from the notification pipeline. + * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. + */ + void maybeCancelSummary(@NonNull NotificationEntry entry); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 6bedd392ef3a..b7efa7c4c5ac 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -17,8 +17,6 @@ package com.android.systemui.wmshell; import android.content.Context; -import android.os.Handler; -import android.view.LayoutInflater; import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; @@ -27,6 +25,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; @@ -53,6 +52,8 @@ public abstract class TvPipModule { PipBoundsState pipBoundsState, PipBoundsHandler pipBoundsHandler, PipTaskOrganizer pipTaskOrganizer, + PipMediaController pipMediaController, + PipNotification pipNotification, WindowManagerShellWrapper windowManagerShellWrapper) { return Optional.of( new PipController( @@ -60,16 +61,16 @@ public abstract class TvPipModule { pipBoundsState, pipBoundsHandler, pipTaskOrganizer, + pipMediaController, + pipNotification, windowManagerShellWrapper)); } @WMSingleton @Provides static PipControlsViewController providePipControlsViewController( - PipControlsView pipControlsView, PipController pipController, - LayoutInflater layoutInflater, Handler handler) { - return new PipControlsViewController(pipControlsView, pipController, layoutInflater, - handler); + PipControlsView pipControlsView, PipController pipController) { + return new PipControlsViewController(pipControlsView, pipController); } @WMSingleton @@ -81,8 +82,8 @@ public abstract class TvPipModule { @WMSingleton @Provides static PipNotification providePipNotification(Context context, - PipController pipController) { - return new PipNotification(context, pipController); + PipMediaController pipMediaController) { + return new PipNotification(context, pipMediaController); } @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 81f448afa13f..91ae08e07677 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -34,6 +34,7 @@ import com.android.wm.shell.common.AnimationThread; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.HandlerExecutor; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; @@ -157,9 +158,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue, - @Main Handler handler, TransactionPool transactionPool) { + ShellExecutor mainExecutor, TransactionPool transactionPool) { return new ShellTaskOrganizer(syncQueue, transactionPool, - new HandlerExecutor(handler), AnimationThread.instance().getExecutor()); + mainExecutor, AnimationThread.instance().getExecutor()); } @BindsOptionalOf @@ -174,4 +175,11 @@ public abstract class WMShellBaseModule { DisplayController displayController) { return Optional.ofNullable(OneHandedController.create(context, displayController)); } + + @WMSingleton + @Provides + static ShellExecutor provideMainShellExecutor(@Main Handler handler) { + return new HandlerExecutor(handler); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index f376df06badd..b6fbd589ae2c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -27,6 +27,7 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; @@ -82,11 +83,12 @@ public class WMShellModule { PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler, PipBoundsState pipBoundsState, PipMediaController pipMediaController, PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper) { + PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, - windowManagerShellWrapper)); + windowManagerShellWrapper, mainExecutor)); } @WMSingleton diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index e73ed801b7df..e6479ddaedb3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -439,7 +439,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt()); - verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt(), any()); + verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt()); } @Test @@ -466,7 +466,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt()); - verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt(), any()); + verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index e24f4ca3581d..a95396cccd66 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -132,7 +132,7 @@ public class UdfpsControllerTest extends SysuiTestCase { verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); - assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mUdfpsSensorId); + assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mSensorProps.sensorId); } private void setUpResources() { @@ -222,8 +222,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); event.recycle(); // THEN the event is passed to the FingerprintManager - verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0), - eq(0f), eq(0f)); + verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), + eq(0), eq(0f), eq(0f)); // AND the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); } @@ -236,8 +236,8 @@ public class UdfpsControllerTest extends SysuiTestCase { // WHEN fingerprint is requested because of AOD interrupt mUdfpsController.onAodInterrupt(0, 0); // THEN the event is passed to the FingerprintManager - verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0), - anyFloat(), anyFloat()); + verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), + eq(0), anyFloat(), anyFloat()); // AND the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 0c872db45194..31c08ae471ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -102,10 +102,10 @@ public class BubbleDataTest extends SysuiTestCase { private ArgumentCaptor<BubbleData.Update> mUpdateCaptor; @Mock - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; @Mock - private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener; + private Bubbles.PendingIntentCanceledListener mPendingIntentCanceledListener; @Before public void setUp() throws Exception { @@ -171,12 +171,11 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); // Verify verifyUpdateReceived(); - assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE); + assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_USER_GESTURE); } @Test @@ -269,7 +268,7 @@ public class BubbleDataTest extends SysuiTestCase { sendUpdatedEntryAtTime(mEntryC1, 6000); verifyUpdateReceived(); - assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED); + assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_AGED); assertOverflowChangedTo(ImmutableList.of(mBubbleA1)); Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1, null /* persistedBubble */); @@ -277,7 +276,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/, true /* showInShade */); verifyUpdateReceived(); - assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED); + assertBubbleRemoved(mBubbleA2, Bubbles.DISMISS_AGED); assertOverflowChangedTo(ImmutableList.of(mBubbleA2)); } @@ -294,14 +293,12 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); mBubbleData.setMaxOverflowBubbles(1); - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of(mBubbleA1)); // Overflow max of 1 is reached; A1 is oldest, so it gets removed - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of(mBubbleA2)); } @@ -322,14 +319,12 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), - BubbleController.DISMISS_NOTIF_CANCEL); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of(mBubbleA2)); // Test - mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), - BubbleController.DISMISS_GROUP_CANCELLED); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_GROUP_CANCELLED); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of()); } @@ -409,8 +404,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); // TODO: this should fail if things work as I expect them to? assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1); @@ -430,8 +424,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOrderNotChanged(); } @@ -450,8 +443,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); verifyUpdateReceived(); assertSelectionChangedTo(mBubbleB2); } @@ -545,8 +537,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); // Verify the selection was cleared. verifyUpdateReceived(); @@ -646,8 +637,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryB2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleA1); } @@ -671,13 +661,11 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertSelectionChangedTo(mBubbleB1); - mBubbleData.dismissBubbleWithKey( - mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryB1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertSelectionChangedTo(mBubbleA1); } @@ -791,8 +779,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertExpandedChangedTo(false); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java index 29ead593c51a..690a1ad42861 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java @@ -60,7 +60,7 @@ public class BubbleTest extends SysuiTestCase { private Bubble mBubble; @Mock - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; @Before public void setUp() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java deleted file mode 100644 index aaeee16dc1fd..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.bubbles; - -import android.app.INotificationManager; -import android.content.Context; -import android.content.pm.LauncherApps; -import android.os.Handler; -import android.view.WindowManager; - -import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.model.SysUiState; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.common.FloatingContentCoordinator; - -/** - * Testable BubbleController subclass that immediately synchronizes surfaces. - */ -public class TestableBubbleController extends BubbleController { - - // Let's assume surfaces can be synchronized immediately. - TestableBubbleController(Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, - BubbleData data, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager lockscreenUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, - FloatingContentCoordinator floatingContentCoordinator, - BubbleDataRepository dataRepository, - SysUiState sysUiState, - INotificationManager notificationManager, - IStatusBarService statusBarService, - WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, - LauncherApps launcherApps, - BubbleLogger bubbleLogger, - Handler mainHandler, - ShellTaskOrganizer shellTaskOrganizer, - BubblePositioner positioner) { - super(context, - notificationShadeWindowController, statusBarStateController, shadeController, - data, Runnable::run, configurationController, interruptionStateProvider, - zenModeController, lockscreenUserManager, groupManager, entryManager, - notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, - dataRepository, sysUiState, notificationManager, statusBarService, - windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger, - mainHandler, shellTaskOrganizer, positioner); - setInflateSynchronously(true); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 6ceac131ed4b..0d352c1b42d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -447,6 +447,25 @@ public class MediaOutputControllerTest extends SysuiTestCase { } @Test + public void getNotificationLargeIcon_withoutLargeIcon_returnsNull() { + final List<NotificationEntry> entryList = new ArrayList<>(); + final NotificationEntry entry = mock(NotificationEntry.class); + final StatusBarNotification sbn = mock(StatusBarNotification.class); + final Notification notification = mock(Notification.class); + entryList.add(entry); + + when(mNotificationEntryManager.getActiveNotificationsForCurrentUser()) + .thenReturn(entryList); + when(entry.getSbn()).thenReturn(sbn); + when(sbn.getNotification()).thenReturn(notification); + when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(notification.hasMediaSession()).thenReturn(true); + when(notification.getLargeIcon()).thenReturn(null); + + assertThat(mMediaOutputController.getNotificationIcon()).isNull(); + } + + @Test public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() { final List<NotificationEntry> entryList = new ArrayList<>(); final NotificationEntry entry = mock(NotificationEntry.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java index aeb625c98283..152c51e1f9f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java @@ -83,7 +83,7 @@ public class RankingBuilder { mCanBubble = ranking.canBubble(); mIsVisuallyInterruptive = ranking.visuallyInterruptive(); mIsConversation = ranking.isConversation(); - mShortcutInfo = ranking.getShortcutInfo(); + mShortcutInfo = ranking.getConversationShortcutInfo(); mRankingAdjustment = ranking.getRankingAdjustment(); mIsBubble = ranking.isBubble(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index d835123e4cad..cd46dda772e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -116,7 +116,7 @@ public class NotificationFilterTest extends SysuiTestCase { new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class)))); + Optional.of(mock(Bubbles.class)))); mDependency.injectMockDependency(ShadeController.class); mDependency.injectMockDependency(NotificationLockscreenUserManager.class); mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 61edcf9c200c..4698b8e50efb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -76,12 +76,12 @@ import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.bubbles.BubblesTestActivity; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Rule; @@ -137,7 +137,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Mock private OnUserInteractionCallback mOnUserInteractionCallback; @Mock - private Bubbles mBubbles; + private BubblesManager mBubblesManager; @Mock private LauncherApps mLauncherApps; @Mock @@ -255,7 +255,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon); assertEquals(mIconDrawable, view.getDrawable()); } @@ -279,7 +279,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -330,7 +330,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertTrue(textView.getText().toString().contains(group.getName())); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -355,7 +355,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); assertEquals(GONE, textView.getVisibility()); @@ -379,7 +379,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); } @@ -414,7 +414,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Proxied")); @@ -442,7 +442,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -468,7 +468,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -495,7 +495,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, false, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -520,7 +520,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View view = mNotificationInfo.findViewById(R.id.silence); assertThat(view.isSelected()).isTrue(); } @@ -548,7 +548,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -579,7 +579,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -609,7 +609,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -653,7 +653,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mTestableLooper.processAllMessages(); @@ -696,7 +696,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View silence = mNotificationInfo.findViewById(R.id.silence); @@ -740,7 +740,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -777,7 +777,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -813,7 +813,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -851,7 +851,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -887,7 +887,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -923,7 +923,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -958,7 +958,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View silence = mNotificationInfo.findViewById(R.id.silence); silence.performClick(); @@ -992,7 +992,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage( anyString(), anyInt(), any(), eq(CONVERSATION_ID)); @@ -1017,7 +1017,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( anyString(), anyInt(), any(), eq(CONVERSATION_ID)); @@ -1052,7 +1052,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); @@ -1092,7 +1092,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 8a5afe6ce667..dbaf5c467c45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplies; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.wmshell.BubblesManager; import org.junit.After; import org.junit.Before; @@ -96,6 +97,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.util.Optional; import java.util.concurrent.CountDownLatch; /** @@ -243,7 +245,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { true, null, mFalsingManager, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + Optional.of(mock(BubblesManager.class)) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index bbc1df21237f..3000b8b449c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -67,7 +67,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -81,6 +80,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Ignore; @@ -131,7 +131,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private ChannelEditorDialogController mChannelEditorDialogController; @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; @Mock private UserContextProvider mContextTracker; - @Mock private Bubbles mBubbles; + @Mock private BubblesManager mBubblesManager; @Mock(answer = Answers.RETURNS_SELF) private PriorityOnboardingDialogController.Builder mBuilder; private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder; @@ -156,7 +156,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, mINotificationManager, mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, mProvider, - mAssistantFeedbackController, Optional.of(mBubbles), + mAssistantFeedbackController, Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback); mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mCheckSaveListener, mOnSettingsClickListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 847e0a474a6a..48375e02f64f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.wmshell.BubblesManager; import org.mockito.ArgumentCaptor; @@ -121,7 +122,7 @@ public class NotificationTestHelper { mGroupMembershipManager = new NotificationGroupManagerLegacy( mStatusBarStateController, () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class))); + Optional.of((mock(Bubbles.class)))); mGroupExpansionManager = mGroupMembershipManager; mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController, mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class), @@ -430,7 +431,8 @@ public class NotificationTestHelper { mock(FalsingManager.class), mStatusBarStateController, mPeopleNotificationIdentifier, - mock(OnUserInteractionCallback.class)); + mock(OnUserInteractionCallback.class), + Optional.of(mock(BubblesManager.class))); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java index 7d84f86cc7b3..b0086ef1d4fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java @@ -92,7 +92,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { mGroupManager = new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class))); + Optional.of(mock(Bubbles.class))); mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java index 29e445a13e24..f81672ab6a37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java @@ -70,7 +70,7 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase { mGroupManager = new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class))); + Optional.of(mock(Bubbles.class))); mGroupManager.setHeadsUpManager(mHeadsUpManager); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index f7489b1c164a..1f31fcd2a2bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -53,7 +53,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; @@ -77,6 +76,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Test; @@ -116,7 +116,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private Handler mHandler; @Mock - private Bubbles mBubbles; + private BubblesManager mBubblesManager; @Mock private ShadeControllerImpl mShadeController; @Mock @@ -193,7 +193,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mock(KeyguardManager.class), mock(IDreamManager.class), - Optional.of(mBubbles), + Optional.of(mBubblesManager), () -> mAssistManager, mRemoteInputManager, mock(NotificationGroupManagerLegacy.class), @@ -280,7 +280,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); // Then - verify(mBubbles).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry())); + verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry())); // This is called regardless, and simply short circuits when there is nothing to do. verify(mShadeController, atLeastOnce()).collapsePanel(); @@ -312,7 +312,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); // Then - verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry()); + verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry())); verify(mShadeController, atLeastOnce()).collapsePanel(); @@ -342,7 +342,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); // Then - verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry()); + verify(mBubblesManager).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry()); verify(mShadeController, atLeastOnce()).collapsePanel(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index d6a7acbcbd78..9f096dada6f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -145,6 +145,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -223,6 +224,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private UserSwitcherController mUserSwitcherController; @Mock private NetworkController mNetworkController; @Mock private VibratorHelper mVibratorHelper; + @Mock private BubblesManager mBubblesManager; @Mock private Bubbles mBubbles; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationIconAreaController mNotificationIconAreaController; @@ -377,6 +379,7 @@ public class StatusBarTest extends SysuiTestCase { wakefulnessLifecycle, mStatusBarStateController, mVibratorHelper, + Optional.of(mBubblesManager), Optional.of(mBubbles), mVisualStabilityManager, mDeviceProvisionedController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index d9e9a8b26f0f..88d04011e738 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bubbles; +package com.android.systemui.wmshell; import static android.app.Notification.FLAG_BUBBLE; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; @@ -64,6 +64,14 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.Bubble; +import com.android.systemui.bubbles.BubbleData; +import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.bubbles.BubbleEntry; +import com.android.systemui.bubbles.BubbleLogger; +import com.android.systemui.bubbles.BubblePositioner; +import com.android.systemui.bubbles.BubbleStackView; +import com.android.systemui.bubbles.Bubbles; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -112,12 +120,12 @@ import java.util.List; /** * Tests the NotificationEntryManager setup with BubbleController. * The {@link NotifPipeline} setup with BubbleController is tested in - * {@link NewNotifPipelineBubbleControllerTest}. + * {@link NewNotifPipelineBubblesTest}. */ @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class BubbleControllerTest extends SysuiTestCase { +public class BubblesTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock @@ -157,6 +165,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor; + private BubblesManager mBubblesManager; private TestableBubbleController mBubbleController; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotificationEntryListener mEntryListener; @@ -167,9 +176,12 @@ public class BubbleControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mRow3; private ExpandableNotificationRow mNonBubbleNotifRow; + private BubbleEntry mBubbleEntry; + private BubbleEntry mBubbleEntry2; + private BubbleEntry mBubbleEntry3; @Mock - private BubbleController.BubbleExpandListener mBubbleExpandListener; + private Bubbles.BubbleExpandListener mBubbleExpandListener; @Mock private PendingIntent mDeleteIntent; @Mock @@ -226,6 +238,9 @@ public class BubbleControllerTest extends SysuiTestCase { mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent); mNonBubbleNotifRow = mNotificationTestHelper.createRow(); + mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry()); + mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry()); + mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3.getEntry()); // Return non-null notification data from the NEM when(mNotificationEntryManager @@ -258,26 +273,13 @@ public class BubbleControllerTest extends SysuiTestCase { mock(HeadsUpManager.class), mock(Handler.class) ); + when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mBubbleController = new TestableBubbleController( mContext, - mNotificationShadeWindowController, - mStatusBarStateController, - mShadeController, mBubbleData, - mConfigurationController, - interruptionStateProvider, - mZenModeController, - mLockscreenUserManager, - mNotificationGroupManager, - mNotificationEntryManager, - mNotifPipeline, - mFeatureFlagsOldPipeline, - mDumpManager, mFloatingContentCoordinator, mDataRepository, - mSysUiState, - mock(INotificationManager.class), mStatusBarService, mWindowManager, mWindowManagerShellWrapper, @@ -288,6 +290,25 @@ public class BubbleControllerTest extends SysuiTestCase { mPositioner); mBubbleController.setExpandListener(mBubbleExpandListener); + mBubblesManager = new BubblesManager( + mContext, + mBubbleController, + mNotificationShadeWindowController, + mStatusBarStateController, + mShadeController, + mConfigurationController, + mStatusBarService, + mock(INotificationManager.class), + interruptionStateProvider, + mZenModeController, + mLockscreenUserManager, + mNotificationGroupManager, + mNotificationEntryManager, + mNotifPipeline, + mSysUiState, + mFeatureFlagsOldPipeline, + mDumpManager); + // Get a reference to the BubbleController's entry listener verify(mNotificationEntryManager, atLeastOnce()) .addNotificationEntryListener(mEntryListenerCaptor.capture()); @@ -300,7 +321,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testAddBubble() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mSysUiStateBubblesExpanded); @@ -309,20 +330,20 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testHasBubbles() { assertFalse(mBubbleController.hasBubbles()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mSysUiStateBubblesExpanded); } @Test public void testRemoveBubble() { - mBubbleController.updateBubble(mRow.getEntry()); - assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); + mBubbleController.updateBubble(mBubbleEntry); + assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())); assertTrue(mBubbleController.hasBubbles()); verify(mNotificationEntryManager).updateNotifications(any()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); verify(mNotificationEntryManager, times(2)).updateNotifications(anyString()); @@ -331,14 +352,14 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testPromoteBubble_autoExpand() throws Exception { - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleController.updateBubble(mBubbleEntry); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey())) .thenReturn(mRow2.getEntry()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey()); assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b)); @@ -361,18 +382,18 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testCancelOverflowBubble() { - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */ + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ true); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey())) .thenReturn(mRow2.getEntry()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL); verify(mNotificationEntryManager, times(1)).performRemoveNotification( eq(mRow.getEntry().getSbn()), any(), anyInt()); assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); @@ -381,11 +402,11 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testUserChange_doesNotRemoveNotif() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_CHANGED); verify(mNotificationEntryManager, never()).performRemoveNotification( eq(mRow.getEntry().getSbn()), any(), anyInt()); assertFalse(mBubbleController.hasBubbles()); @@ -395,15 +416,15 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDismissStack() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); verify(mNotificationEntryManager, times(1)).updateNotifications(any()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry2); verify(mNotificationEntryManager, times(2)).updateNotifications(any()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mNotificationEntryManager, times(3)).updateNotifications(any()); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); @@ -417,12 +438,12 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); @@ -434,7 +455,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Make sure the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Collapse mBubbleController.collapseStack(); @@ -450,15 +471,15 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); mEntryListener.onPendingEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -472,7 +493,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Last added is the one that is expanded assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Switch which bubble is expanded mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( @@ -481,7 +502,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // collapse for previous bubble verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( @@ -501,12 +522,12 @@ public class BubbleControllerTest extends SysuiTestCase { public void testExpansionRemovesShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -520,7 +541,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -529,12 +550,12 @@ public class BubbleControllerTest extends SysuiTestCase { public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -548,7 +569,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -558,7 +579,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Nothing should have changed // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -568,8 +589,8 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); mEntryListener.onPendingEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -584,13 +605,13 @@ public class BubbleControllerTest extends SysuiTestCase { assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Dismiss currently expanded mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); // Make sure first bubble is selected @@ -602,7 +623,7 @@ public class BubbleControllerTest extends SysuiTestCase { mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey()); @@ -619,7 +640,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, @@ -636,7 +657,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, @@ -653,11 +674,11 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the suppress notif bubble mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -667,22 +688,22 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testSuppressNotif_onUpdateNotif() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Should not be suppressed assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should show dot assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); // Update to suppress notif setMetadataFlags(mRow.getEntry(), Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -695,13 +716,13 @@ public class BubbleControllerTest extends SysuiTestCase { final String key = mRow.getEntry().getKey(); mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Simulate notification cancellation. mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL); - mBubbleController.expandStackAndSelectBubble(mRow.getEntry()); + mBubbleController.expandStackAndSelectBubble(mBubbleEntry); assertTrue(mSysUiStateBubblesExpanded); } @@ -710,7 +731,7 @@ public class BubbleControllerTest extends SysuiTestCase { public void testMarkNewNotificationAsShowInShade() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -726,31 +747,31 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED); verify(mDeleteIntent, never()).send(); } @Test public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(1)).send(); } @Test public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } @Test public void testRemoveBubble_noLongerBubbleAfterUpdate() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; @@ -766,7 +787,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_succeeds_appCancel() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); @@ -780,7 +801,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_entryListenerRemove() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); @@ -792,11 +813,11 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void removeBubble_clearAllIntercepted() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL_ALL); @@ -805,17 +826,17 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(intercepted); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); } @Test public void removeBubble_userDismissNotifIntercepted() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL); @@ -824,21 +845,21 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(intercepted); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); } @Test public void removeNotif_inOverflow_intercepted() { // Get bubble with notif in shade. mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dismiss the bubble into overflow. mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertFalse(mBubbleController.hasBubbles()); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( @@ -852,14 +873,14 @@ public class BubbleControllerTest extends SysuiTestCase { public void removeNotif_notInOverflow_notIntercepted() { // Get bubble with notif in shade. mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NO_LONGER_BUBBLE); + mRow.getEntry().getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE); assertFalse(mBubbleController.hasBubbles()); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( @@ -872,11 +893,11 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() { mBubbleController.updateBubble( - mRow.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); + mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false); mBubbleController.updateBubble( - mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); + mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false); mBubbleController.updateBubble( - mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); + mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey())) @@ -887,12 +908,12 @@ public class BubbleControllerTest extends SysuiTestCase { mBubbleData.setMaxOverflowBubbles(1); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertEquals(mBubbleData.getBubbles().size(), 2); assertEquals(mBubbleData.getOverflowBubbles().size(), 1); mBubbleController.removeBubble( - mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow2.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); // Overflow max of 1 is reached; mRow is oldest, so it gets removed verify(mNotificationEntryManager, times(1)).performRemoveNotification( eq(mRow.getEntry().getSbn()), any(), eq(REASON_CANCEL)); @@ -902,22 +923,22 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onPendingEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -926,21 +947,21 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onPendingEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mBubbleData.setExpanded(true); // Once a bubble is expanded the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -959,11 +980,11 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN the summary and bubbled child are suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -979,7 +1000,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // GIVEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // WHEN the summary is cancelled by the app mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL); @@ -1002,7 +1023,7 @@ public class BubbleControllerTest extends SysuiTestCase { groupSummary.addChildNotification(groupedBubble); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN only the NON-bubble children are dismissed List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren(); @@ -1017,7 +1038,8 @@ public class BubbleControllerTest extends SysuiTestCase { // THEN the bubble child is suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); // THEN the summary is removed from GroupManager verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); @@ -1031,18 +1053,18 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() { // Setup - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); assertTrue(mBubbleController.hasBubbles()); // Overflow it mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue(); // Test - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index b9394ff3f26a..99c8ca417778 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bubbles; +package com.android.systemui.wmshell; import static android.app.Notification.FLAG_BUBBLE; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; @@ -46,7 +46,6 @@ import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; import android.hardware.display.AmbientDisplayConfiguration; -import android.hardware.face.FaceManager; import android.os.Handler; import android.os.PowerManager; import android.service.dreams.IDreamManager; @@ -60,8 +59,14 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.BubbleData; +import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.bubbles.BubbleEntry; +import com.android.systemui.bubbles.BubbleLogger; +import com.android.systemui.bubbles.BubblePositioner; +import com.android.systemui.bubbles.BubbleStackView; +import com.android.systemui.bubbles.Bubbles; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -69,9 +74,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RankingBuilder; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; @@ -81,10 +84,8 @@ import com.android.systemui.statusbar.notification.collection.legacy.Notificatio import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; @@ -92,7 +93,6 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.util.InjectionInflationController; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -111,18 +111,18 @@ import java.util.List; /** * Tests the NotifPipeline setup with BubbleController. * The NotificationEntryManager setup with BubbleController is tested in - * {@link BubbleControllerTest}. + * {@link BubblesTest}. */ @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { +public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock - private BubbleController.NotifCallback mNotifCallback; + private BubblesManager.NotifCallback mNotifCallback; @Mock private WindowManager mWindowManager; @Mock @@ -136,8 +136,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock private ZenModeConfig mZenModeConfig; @Mock - private FaceManager mFaceManager; - @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private SysuiStatusBarStateController mStatusBarStateController; @@ -156,6 +154,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; + private BubblesManager mBubblesManager; private TestableBubbleController mBubbleController; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotifCollectionListener mEntryListener; @@ -163,8 +162,10 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mNonBubbleNotifRow; + private BubbleEntry mBubbleEntry; + private BubbleEntry mBubbleEntry2; @Mock - private BubbleController.BubbleExpandListener mBubbleExpandListener; + private Bubbles.BubbleExpandListener mBubbleExpandListener; @Mock private PendingIntent mDeleteIntent; @Mock @@ -174,16 +175,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock private ShadeController mShadeController; @Mock - private NotificationShelfComponent mNotificationShelfComponent; - @Mock private NotifPipeline mNotifPipeline; @Mock private FeatureFlags mFeatureFlagsNewPipeline; @Mock private DumpManager mDumpManager; @Mock - private LockscreenLockIconController mLockIconController; - @Mock private IStatusBarService mStatusBarService; @Mock private LauncherApps mLauncherApps; @@ -197,7 +194,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private BubbleData mBubbleData; private TestableLooper mTestableLooper; - private SuperStatusBarViewFactory mSuperStatusBarViewFactory; @Before public void setUp() throws Exception { @@ -205,26 +201,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); - mContext.addMockSystemService(FaceManager.class, mFaceManager); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext, - new InjectionInflationController(SystemUIFactory.getInstance().getSysUIComponent() - .createViewInstanceCreatorFactory()), - new NotificationShelfComponent.Builder() { - @Override - public NotificationShelfComponent.Builder notificationShelf( - NotificationShelf view) { - return this; - } - - @Override - public NotificationShelfComponent build() { - return mNotificationShelfComponent; - } - }, - mLockIconController); - mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, @@ -240,6 +218,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mRow = mNotificationTestHelper.createBubble(mDeleteIntent); mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); mNonBubbleNotifRow = mNotificationTestHelper.createRow(); + mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry()); + mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry()); mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); @@ -265,23 +245,9 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); mBubbleController = new TestableBubbleController( mContext, - mNotificationShadeWindowController, - mStatusBarStateController, - mShadeController, mBubbleData, - mConfigurationController, - interruptionStateProvider, - mZenModeController, - mLockscreenUserManager, - mNotificationGroupManager, - mNotificationEntryManager, - mNotifPipeline, - mFeatureFlagsNewPipeline, - mDumpManager, mFloatingContentCoordinator, mDataRepository, - mSysUiState, - mock(INotificationManager.class), mStatusBarService, mWindowManager, mWindowManagerShellWrapper, @@ -290,9 +256,28 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mock(Handler.class), mock(ShellTaskOrganizer.class), mPositioner); - mBubbleController.addNotifCallback(mNotifCallback); mBubbleController.setExpandListener(mBubbleExpandListener); + mBubblesManager = new BubblesManager( + mContext, + mBubbleController, + mNotificationShadeWindowController, + mStatusBarStateController, + mShadeController, + mConfigurationController, + mStatusBarService, + mock(INotificationManager.class), + interruptionStateProvider, + mZenModeController, + mLockscreenUserManager, + mNotificationGroupManager, + mNotificationEntryManager, + mNotifPipeline, + mSysUiState, + mFeatureFlagsNewPipeline, + mDumpManager); + mBubblesManager.addNotifCallback(mNotifCallback); + // Get a reference to the BubbleController's entry listener verify(mNotifPipeline, atLeastOnce()) .addCollectionListener(mNotifListenerCaptor.capture()); @@ -301,26 +286,26 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testAddBubble() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); } @Test public void testHasBubbles() { assertFalse(mBubbleController.hasBubbles()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); } @Test public void testRemoveBubble() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); } @@ -328,17 +313,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_withDismissedNotif_inOverflow() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Make it look like dismissed notif mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true); // Now remove the bubble mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())); // We don't remove the notification since the bubble is still in overflow. @@ -349,19 +335,20 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_withDismissedNotif_notInOverflow() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Make it look like dismissed notif mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true); // Now remove the bubble mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL); assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())); // Since the notif is dismissed and not in overflow, once the bubble is removed, @@ -373,15 +360,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testDismissStack() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry2); verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mNotifCallback, times(3)).invalidateNotifications(anyString()); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); @@ -393,12 +380,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); @@ -407,7 +394,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); // Make sure the notif is suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Collapse mBubbleController.collapseStack(); @@ -421,15 +409,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); mEntryListener.onEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -440,15 +428,17 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Last added is the one that is expanded assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Switch which bubble is expanded - mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); + mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( + mRow.getEntry().getKey())); mBubbleData.setExpanded(true); assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // collapse for previous bubble verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( @@ -467,11 +457,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testExpansionRemovesShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -483,7 +474,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -492,12 +483,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -509,7 +500,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -519,7 +510,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Nothing should have changed // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -529,8 +520,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); mEntryListener.onEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -543,13 +534,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Dismiss currently expanded mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); // Make sure first bubble is selected @@ -561,7 +552,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey()); @@ -576,7 +567,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, @@ -591,7 +582,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, @@ -606,11 +597,11 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Add the suppress notif bubble mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -618,22 +609,22 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testSuppressNotif_onUpdateNotif() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Should not be suppressed assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should show dot assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); // Update to suppress notif setMetadataFlags(mRow.getEntry(), Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -643,7 +634,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testMarkNewNotificationAsShowInShade() { mEntryListener.onEntryAdded(mRow.getEntry()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -659,31 +650,31 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED); verify(mDeleteIntent, never()).send(); } @Test public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(1)).send(); } @Test public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } @Test public void testRemoveBubble_noLongerBubbleAfterUpdate() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; @@ -699,7 +690,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_entryListenerRemove() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); @@ -711,36 +702,36 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void removeBubble_intercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); - boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry()); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Intercept! assertTrue(intercepted); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); } @Test public void removeBubble_dismissIntoOverflow_intercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dismiss the bubble - mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertFalse(mBubbleController.hasBubbles()); // Dismiss the notification - boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry()); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Intercept dismissal since bubble is going into overflow assertTrue(intercepted); @@ -749,19 +740,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void removeBubble_notIntercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dismiss the bubble - mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL); assertFalse(mBubbleController.hasBubbles()); // Dismiss the notification - boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry()); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Not a bubble anymore so we don't intercept dismissal. assertFalse(intercepted); @@ -769,21 +759,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); - mBubbleController.handleDismissalInterception(mRow.getEntry()); + mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -792,21 +782,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mBubbleData.setExpanded(true); // Once a bubble is expanded the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -825,11 +815,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN the summary and bubbled child are suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -845,7 +836,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // GIVEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // WHEN the summary is cancelled by the app mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0); @@ -868,7 +859,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { groupSummary.addChildNotification(groupedBubble); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN only the NON-bubble children are dismissed List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren(); @@ -882,11 +873,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // THEN the bubble child still exists as a bubble and is suppressed from the shade assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); // THEN the summary is also suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupSummary.getEntry())); + groupSummary.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java new file mode 100644 index 000000000000..2273bc48ce8b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wmshell; + +import android.content.Context; +import android.content.pm.LauncherApps; +import android.os.Handler; +import android.view.WindowManager; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.bubbles.BubbleData; +import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.bubbles.BubbleLogger; +import com.android.systemui.bubbles.BubblePositioner; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.common.FloatingContentCoordinator; + +/** + * Testable BubbleController subclass that immediately synchronizes surfaces. + */ +public class TestableBubbleController extends BubbleController { + + // Let's assume surfaces can be synchronized immediately. + TestableBubbleController(Context context, + BubbleData data, + FloatingContentCoordinator floatingContentCoordinator, + BubbleDataRepository dataRepository, + IStatusBarService statusBarService, + WindowManager windowManager, + WindowManagerShellWrapper windowManagerShellWrapper, + LauncherApps launcherApps, + BubbleLogger bubbleLogger, + Handler mainHandler, + ShellTaskOrganizer shellTaskOrganizer, + BubblePositioner positioner) { + super(context, data, Runnable::run, floatingContentCoordinator, dataRepository, + statusBarService, windowManager, windowManagerShellWrapper, launcherApps, + bubbleLogger, mainHandler, shellTaskOrganizer, positioner); + setInflateSynchronously(true); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java index 17dc76b38a56..7847c57dbc32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bubbles; +package com.android.systemui.wmshell; import android.content.ContentResolver; import android.hardware.display.AmbientDisplayConfiguration; diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp deleted file mode 100644 index 5526c657b874..000000000000 --- a/packages/Tethering/Android.bp +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -java_defaults { - name: "TetheringAndroidLibraryDefaults", - sdk_version: "module_current", - srcs: [ - "src/**/*.java", - ":framework-tethering-shared-srcs", - ":tethering-module-utils-srcs", - ":services-tethering-shared-srcs", - ], - static_libs: [ - "androidx.annotation_annotation", - "netd_aidl_interface-unstable-java", - "netlink-client", - // TODO: use networkstack-client instead of just including the AIDL interface - "networkstack-aidl-interfaces-unstable-java", - "android.hardware.tetheroffload.config-V1.0-java", - "android.hardware.tetheroffload.control-V1.0-java", - "net-utils-framework-common", - "net-utils-device-common", - ], - libs: [ - "framework-statsd.stubs.module_lib", - "framework-tethering.impl", - "framework-wifi", - "unsupportedappusage", - ], - plugins: ["java_api_finder"], - manifest: "AndroidManifestBase.xml", -} - -// Build tethering static library, used to compile both variants of the tethering. -android_library { - name: "TetheringApiCurrentLib", - defaults: ["TetheringAndroidLibraryDefaults"], -} - -// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK). -cc_library { - name: "libtetherutilsjni", - sdk_version: "current", - apex_available: [ - "//apex_available:platform", // Used by InProcessTethering - "com.android.tethering", - ], - min_sdk_version: "current", - srcs: [ - "jni/android_net_util_TetheringUtils.cpp", - ], - shared_libs: [ - "liblog", - "libnativehelper_compat_libc++", - ], - - // We cannot use plain "libc++" here to link libc++ dynamically because it results in: - // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found - // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't - // build because soong complains of: - // module Tethering missing dependencies: libc++_shared - // - // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries - // we depend on do not dynamically link libc++. This is currently the case, because liblog is - // C-only and libnativehelper_compat_libc also uses stl: "c++_static". - stl: "c++_static", - - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - "-Wthread-safety", - ], - - ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"], -} - -// Common defaults for compiling the actual APK. -java_defaults { - name: "TetheringAppDefaults", - sdk_version: "module_current", - privileged: true, - jni_libs: [ - "libtetherutilsjni", - ], - resource_dirs: [ - "res", - ], - libs: [ - "framework-tethering", - "framework-wifi", - ], - jarjar_rules: "jarjar-rules.txt", - optimize: { - proguard_flags_files: ["proguard.flags"], - }, -} - -// Non-updatable tethering running in the system server process for devices not using the module -android_app { - name: "InProcessTethering", - defaults: ["TetheringAppDefaults"], - static_libs: ["TetheringApiCurrentLib"], - certificate: "platform", - manifest: "AndroidManifest_InProcess.xml", - // InProcessTethering is a replacement for Tethering - overrides: ["Tethering"], - apex_available: ["com.android.tethering"], - min_sdk_version: "current", -} - -// Updatable tethering packaged as an application -android_app { - name: "Tethering", - defaults: ["TetheringAppDefaults"], - static_libs: ["TetheringApiCurrentLib"], - certificate: "networkstack", - manifest: "AndroidManifest.xml", - use_embedded_native_libs: true, - // The permission configuration *must* be included to ensure security of the device - required: ["NetworkPermissionConfig"], - apex_available: ["com.android.tethering"], - min_sdk_version: "current", -} diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml deleted file mode 100644 index e6444f3ead5c..000000000000 --- a/packages/Tethering/AndroidManifest.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.tethering" - android:sharedUserId="android.uid.networkstack"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> - - <!-- Permissions must be defined here, and not in the base manifest, as the tethering - running in the system server process does not need any permission, and having - privileged permissions added would cause crashes on startup unless they are also - added to the privileged permissions allowlist for that package. --> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.BLUETOOTH" /> - <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> - <uses-permission android:name="android.permission.BROADCAST_STICKY" /> - <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.MANAGE_USB" /> - <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> - <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> - <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> - <uses-permission android:name="android.permission.READ_PHONE_STATE"/> - <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> - <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> - <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> - - <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" /> - - <application - android:process="com.android.networkstack.process" - android:extractNativeLibs="false" - android:persistent="true"> - <service android:name="com.android.networkstack.tethering.TetheringService" - android:permission="android.permission.MAINLINE_NETWORK_STACK" - android:exported="true"> - <intent-filter> - <action android:name="android.net.ITetheringConnector"/> - </intent-filter> - </service> - </application> -</manifest> diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml deleted file mode 100644 index 97c3988829fe..000000000000 --- a/packages/Tethering/AndroidManifestBase.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.tethering" - android:versionCode="1" - android:versionName="R-initial"> - <application - android:label="Tethering" - android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true" - android:usesCleartextTraffic="true"> - </application> -</manifest> diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml deleted file mode 100644 index b1f124097c79..000000000000 --- a/packages/Tethering/AndroidManifest_InProcess.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.tethering.inprocess" - android:sharedUserId="android.uid.system" - android:process="system"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> - <application> - <service android:name="com.android.networkstack.tethering.TetheringService" - android:process="system" - android:permission="android.permission.MAINLINE_NETWORK_STACK" - android:exported="true"> - <intent-filter> - <action android:name="android.net.ITetheringConnector.InProcess"/> - </intent-filter> - </service> - </application> -</manifest> diff --git a/packages/Tethering/OWNERS b/packages/Tethering/OWNERS deleted file mode 100644 index 5b42d490411e..000000000000 --- a/packages/Tethering/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -include platform/packages/modules/NetworkStack/:/OWNERS -markchien@google.com diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING deleted file mode 100644 index 5617b0c13c1c..000000000000 --- a/packages/Tethering/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "TetheringTests" - } - ], - "postsubmit": [ - { - "name": "TetheringIntegrationTests" - } - ] -} diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp deleted file mode 100644 index 05243749f765..000000000000 --- a/packages/Tethering/apex/Android.bp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -apex { - name: "com.android.tethering", - updatable: true, - min_sdk_version: "current", - java_libs: ["framework-tethering"], - bpfs: ["offload.o"], - apps: ["Tethering"], - manifest: "manifest.json", - key: "com.android.tethering.key", - - androidManifest: "AndroidManifest.xml", -} - -apex_key { - name: "com.android.tethering.key", - public_key: "com.android.tethering.avbpubkey", - private_key: "com.android.tethering.pem", -} - -android_app_certificate { - name: "com.android.tethering.certificate", - certificate: "com.android.tethering", -} - -override_apex { - name: "com.android.tethering.inprocess", - base: "com.android.tethering", - package_name: "com.android.tethering.inprocess", - apps: [ - "InProcessTethering", - ], -} diff --git a/packages/Tethering/apex/AndroidManifest.xml b/packages/Tethering/apex/AndroidManifest.xml deleted file mode 100644 index 4aae3cc3000d..000000000000 --- a/packages/Tethering/apex/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.tethering"> - <!-- APEX does not have classes.dex --> - <application android:hasCode="false" /> - <!-- b/145383354: Current minSdk is locked to Q for development cycle, lock it to next version - before ship. --> - <!-- TODO: Uncomment this when the R API level is fixed. b/148281152 --> - <!--uses-sdk - android:minSdkVersion="29" - android:targetSdkVersion="29" - /> - --> -</manifest> diff --git a/packages/Tethering/apex/com.android.tethering.avbpubkey b/packages/Tethering/apex/com.android.tethering.avbpubkey Binary files differdeleted file mode 100644 index 9a2c0174e496..000000000000 --- a/packages/Tethering/apex/com.android.tethering.avbpubkey +++ /dev/null diff --git a/packages/Tethering/apex/com.android.tethering.pem b/packages/Tethering/apex/com.android.tethering.pem deleted file mode 100644 index d4f39abd3bb2..000000000000 --- a/packages/Tethering/apex/com.android.tethering.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEA+AWTp03PBRMGt4mVNLt5PDoFFSfmFOVTM7jt5AJXnQMIDsAM -1cyWGWRridGIpoHAaCALVgW5aRySgi8yV5xP4w0YHcKbfh9M6I9oz4RUo4GQBZfX -+lFIGaLjb6I3tEJxPuxps4sW26Io63ihwTnKeGyADHdHGWDUs9WU0Ml+QTvKrdjy -qC03M0dehYXILGiA9m+UXwKoKxhWgfDUhWLhDBUtLJLPL4WeqKc9sG9h+zzVqE+8 -LzJsfrodKhTTrLpWOXi6YLRTk8dzsuPz/Nu98sJd1w3fHd20DrmkqsxVhgN1h+nk -zcPpxyGYIP6qYVZCmIXCwZZNtPeb7y/tOs967VHoZ4Qj7p2tE0CAWFMZFGjA/pcZ -7fi6CsIuMOYBbj4+wRlJwpG1g5zSJBCjzhv7dZp8S5oXmLShNYOMYEdsPfaZbm08 -3pVY+k8DVf7idcANXNw1lM+sPbE2hp5VuEuVpK+ca5x8hIMpTqJ84wDAjnC1kCwm -X2xfNvYPKNF58SvqlNCPN8X7hQjoeaEb7w24vCdZMRqeGBmu1GNQvCyzbBO0huQm -f5CQPrZjPcnoImlP879VPxY4YB6tAjsA/ZLiub9VdT108lCjb5r8criMzpMAA/AQ -NqQLWFI3M43xPemGBTiIguTYgpRgGcdRZf7XuTgTY5qzQZZuZMVuwaqSD2cCAwEA -AQKCAgEA0jMvw3BPTrakT7Lb8JgelKt7mUV6WyVMUZ6eh0pw5JIoJxAfEKfWYmjY -NzKNRMjcv6LA2MP7MplTld/YI6ZHkl+Lm9VOISL39HVuV8mIThbFb+gT1INEvu1t -IjRyT2SsQ67rmo377mLNmVtgg7mt3kfecjI44MpPGqad/CF4zmKVUKd4aI4BpYUM -F8+dKf3bpoBEWA2RZwy2bGQmSXHW132vDoLR8y2knL04rCqJ+PrC/WWuULXEe9bS -VtLV3yMBZq3qD4Fk/+7fILLPGvNFVdPi4htQiChYrM4rP9HzfaO63VieYMF0hR70 -pqoOznXj9Q4QVC9FZmUgFCQjQ1+KhqJw3OldIo0SnvpsLdTO/inKkhQWKC5HlPyh -/rqvro2j3pTHWPAziuBr+oQPcdVCOlCBZ+B99L1tO7aGktVPEIVQG7G7jlFMBiJ1 -j/kRGk2RTX8RaPQJTnwUqp8mWUV2fwxHiXNadjejA5ZU3eQT2eAOhXl1w6Lv2jEl -0wMOwPMJGcF77CcqnnWHON8fkxCbAfyy5Uo6Pm9g/Zzecn+ji2sabG7Ge5t0gzdL -LKRcGoyakN2CrbQ8pxlCTgE4HX5oPY+VuqOf8L3AIWIJBsyLbXHVkL1mqQ/Ed2uz -zaaSFYUZw81+m/5bl8JLPaIFNPyikZrXTD0YRer3V06XiyP/kYECggEBAP033xeF -OhgRwkRTjd68hwRJpyHsZDWxHiUqQf6l6yFv5mEE355G2IGI7cZmR2+tUDjQdxLv -tAZIszTK4PFCdVTeWfGVFbVF84eNWLB124pHDMM79GN/AMcuHnQPR756a8IO1hIy -4KxIUE1a1PKN5b9IgE5Lu4TZM96HDpFcUAmCT5urdYDmg3++IWT9PYQlGS7Hhiar -r+Hh646waM8Qx619CwXBqy+Y37+WHVbYqJClr6AcpVMrGA+6cgpskFpZAPLsoy7G -RSsVfyV8pH2JKm/hzk7XCwIpczxeWQSfpJWZ+oOPFHu+zM60Cdj2UrQyKrNHwew8 -+WYe9eCA+MiNBcECggEBAPq/F1vdqROiLv9uzhKb8ybgdL7CmREELiqwK+MvNE9t -W7lQz7lcWzav+b2n0M+VJBxUWB3XClgoIvA/AllgTgsYXfKAxNakhKLSBoMmvKCW -HtWcGr/D3RcmacK+DTMWlVS/LuueAFLuH6UmBIUFKc+qA5x7oQecAFALBFupE3G4 -LtAspLBI6P8gRtRav5p2whs9H8qjYcyf2f6liWpkmFITcXvPvAxFHicR6ZJdwZ/S -PiX2LJQnOpT7L3+2PWnYwzFStb4MkMGlFKcscU9CvS53JcP/J4Asjk0I4zDB2gri -xzFHPlVzCr2IVVGptKCQ3sdYiMIzQKzEXQHCU8h37ycCggEBAJu8aC48Fz3Edlm1 -ldS+2L9vWSaJEBzhoSu0cMBgZVu8SdGzwKDE69XHVI4oS5lI28UFmaaA3JTc07MN -cAmSGT2oP2NQkPhbXGsrKLfm1K6YAiZ1Ulp7OwxFth8lYreo7Wt92nV46yuqkhDx -Y3UGhp39xkPhWiRbvgYHxJLsVqFyjumsK2mq3IeNdVZ6VgJXGsTlnAFeqJ7hZxHs -N5natSRjeosA0PtGJ57agZLvT8Ue0gREef3LzFGoFwmIOcQHZ4kAt2BGOzZDU17H -6Rb4bKxBEbT1l2St/5zKXi90zDHicOvG7Q8qiyY6HrBc1wLSs+ZtpLxZx/3h3tFE -IT6fVUECggEBAMSAQm8Ey76OJ+SXUjk1K50442SnHcs/Cmr7urkEQitImUwl71Pk -87pst/uP6szypOTqmE9yOTIS6iZ6Sn3+QcriIqWrkhZfwW3Tx7S6A7KZUrq15iSH -+thsiw9JXxC9TvOmC8AsBzb2U6hZncsc28JZCxFztSNAduJDb/vhCVLiMxWDFuDr -kmR1R+yc3XDQRpeQFDz6QudYEj9EPOc6xD/16sZLaqP2+oVFvVSt0tJLsdaQECle -gMNGAdhE2eX8MCOUHMc+E6cdlozYAEhMFfO2/cqWR79jq3TlVR3dnOFRDScqHMhc -KnuTvsELjHkUbvGsCSiff7yk+fop7vy4OJsCggEAPemJdItO2rhib8EofrZdY72I -oifX1jhPZ1BWD2GKgcx+eVyJGbONBbJVexvvskTfZBvCcAegmgp+sngP6MO6yZkr -cHMfAJeApYZnshsgXksHGMDtSB50/w1JLrc/nqpxdpy/aTazt0Eu1pLWpze1HFZ/ -Xyu4PcmrU+4P1vN7c396slHMktEvly6QqOn4nfBbGDJ17Ow6X1XFvGjAxQPIDTB+ -6loV14AHymwmqwMrGn84O72rzqyw+41GxW5+oXhOZ4MeXF3u89TBLWvXDpPy/YQU -EiKpodN0YeEn6Ghzplan8rUha+7TP7AYnS5pCszsCHKd03Py0lMLkF+uAfVsDA== ------END RSA PRIVATE KEY----- diff --git a/packages/Tethering/apex/com.android.tethering.pk8 b/packages/Tethering/apex/com.android.tethering.pk8 Binary files differdeleted file mode 100644 index 3b94405945cb..000000000000 --- a/packages/Tethering/apex/com.android.tethering.pk8 +++ /dev/null diff --git a/packages/Tethering/apex/com.android.tethering.x509.pem b/packages/Tethering/apex/com.android.tethering.x509.pem deleted file mode 100644 index a1786e35e854..000000000000 --- a/packages/Tethering/apex/com.android.tethering.x509.pem +++ /dev/null @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGKTCCBBGgAwIBAgIUNiSs5EMqxCZ31gWWCcRJVp9HffAwDQYJKoZIhvcNAQEL -BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH -DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy -b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC50ZXRoZXJpbmcxIjAgBgkqhkiG9w0B -CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjE4MDcwMDQ4WhgPNDc1NzEx -MTMwNzAwNDhaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW -MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE -CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQudGV0aGVyaW5nMSIwIAYJ -KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAxvTUA4seblYjZLfTVNwZuJH914QVNFTj+vD94pWmt5Aq -sH1DVTpBvpXXegc/P5HI2XF/71poSBib1WaQSuXG0fU5K75T18bOGL0qF+fhMtBO -wUyvulcjO0h4XE/xf0txY54exUjAA4JS9ERGJOgb4GOwSbPyzekfmzIyCZ2Yawwu -+oGwD2ZNzZRaPOoWxjwohBWQ6mySuvF9RRRb300qmxxUGFM9Ki3aqrWlYlHEOwOC -M+gIXxYFO7S+yUzf6/gMZLOz2YqfcTOup4hAxtExR7niutxJSsRLPBL237exAJoz -OupoXjtWAlPK4ZwZ/Nl1jdTWauJ+Kv3WqzhHGEb2gn3ZpeO3IdOjJhDgFJ6m1OT/ -kjRbW1LCuKGrKaoqsEDT2X3a7Izfripn65hSNTfR5gNLtgELaI3/vXi8Fmzw1AfH -+qi6ulElZvSwx0qm+S0QiPyGFlxrsdnHoGJl1tzjJW8KdNZRvzRLUQtbphPp+VkL -5i0bNKum+AwbfdUkLkNLfw9XdbujgBkZTZDQbZGsNjgrvyXcPO2KiJee0hVCZRs0 -rhDi5Pfm7BnN/I2vaTRz/W4mdct9H2RWMuqlSH90JvmKtWcND8ahmOJ3sggrvzfO -QNs3k4JTRecamMzqIkylhlnEC4FjWc6Bx4wsEpwBMZOkF/tGGMZYf2C09a8tpP0C -AwEAAaNTMFEwHQYDVR0OBBYEFNP5gIpNWmq0xa411M1GaRPbEijvMB8GA1UdIwQY -MBaAFNP5gIpNWmq0xa411M1GaRPbEijvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI -hvcNAQELBQADggIBADJGmU3QP4EGbt6eBhVPeo/efsqrHsuB2fvFzvIobJbfkSob -cmvjbzIikOlPAgFWj8lT5SDcIWRorFf1u2JylClJ0nSDcqJMHVKmT7wseV/KtX// -1yUyJFRQVzmjC89dp8OIc00GmItivKLer3NbJdkR3rTUjg7+bNUO27Qp3AFREmiJ -P+M7ouvcQRvByUWbp/LOrJpMdJLysRBO562RwrtwTjltdvufyYswbBZOKEiUh1Jc -Ged+3+SJdhwq3Wy+R3Uj7YE7mUMu1QNbANIMrwF8W93EA53eoL2+cKmuaVU6ZURL -xgSJaY6TrunnSI9XTROLtjsFlJorYWy2tvG7Q5Hw3OkO2Xdz/mm85VTkiusg9DMB -WWTv607YtsIO0FhKmcV4bp3q/EkRj3t/zLvL9uFJrWDGkuShZq6fQvqbCvaokOPY -+M0ZRIwgwa9UpEE0BMklVWqR6BGyap614gOgcOjYM70WRNl59Qne+g128ZN7g9nz -61F70i7kUngV0ZUz1/Fu/NCG+6wGF85ZbFmQl60YHPDw1FtjVUuKyBblaDzdJunx -yQr2t9RUokzFBFK0lGW3+yf0WDQ5fqTMs5h8bz1FCq8/HzWmpdOfqePLe4zsld3b -1nFuSohaIfbn/HDdTNtTBGQPgz8ZswQ6ejJJqTLz9D/odbqn9LeIhDZXcQTf ------END CERTIFICATE----- diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json deleted file mode 100644 index 11e205d1b7ab..000000000000 --- a/packages/Tethering/apex/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.tethering", - "version": 309999900 -} diff --git a/packages/Tethering/bpf_progs/Android.bp b/packages/Tethering/bpf_progs/Android.bp deleted file mode 100644 index d54f86148665..000000000000 --- a/packages/Tethering/bpf_progs/Android.bp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (C) 2020 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. -// - -// -// bpf kernel programs -// -bpf { - name: "offload.o", - srcs: ["offload.c"], - cflags: [ - "-Wall", - "-Werror", - ], - include_dirs: [ - // TODO: get rid of system/netd. - "system/netd/bpf_progs", // for bpf_net_helpers.h - "system/netd/libnetdbpf/include", // for bpf_shared.h - "system/netd/libnetdutils/include", // for UidConstants.h - ], -} diff --git a/packages/Tethering/bpf_progs/offload.c b/packages/Tethering/bpf_progs/offload.c deleted file mode 100644 index cc5af3127b02..000000000000 --- a/packages/Tethering/bpf_progs/offload.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <linux/if.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <linux/pkt_cls.h> -#include <linux/tcp.h> - -#include "bpf_helpers.h" -#include "bpf_net_helpers.h" -#include "netdbpf/bpf_shared.h" - -DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64, - AID_NETWORK_STACK) - -// Tethering stats, indexed by upstream interface. -DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK) - -// Tethering data limit, indexed by upstream interface. -// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif]) -DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK) - -static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) { - int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0; - void* data = (void*)(long)skb->data; - const void* data_end = (void*)(long)skb->data_end; - struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet - struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data; - - // Must be meta-ethernet IPv6 frame - if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK; - - // Must have (ethernet and) ipv6 header - if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK; - - // Ethertype - if present - must be IPv6 - if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK; - - // IP version must be 6 - if (ip6->version != 6) return TC_ACT_OK; - - // Cannot decrement during forward if already zero or would be zero, - // Let the kernel's stack handle these cases and generate appropriate ICMP errors. - if (ip6->hop_limit <= 1) return TC_ACT_OK; - - // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness. - __be32 src32 = ip6->saddr.s6_addr32[0]; - if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP - (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast - return TC_ACT_OK; - - TetherIngressKey k = { - .iif = skb->ifindex, - .neigh6 = ip6->daddr, - }; - - TetherIngressValue* v = bpf_tether_ingress_map_lookup_elem(&k); - - // If we don't find any offload information then simply let the core stack handle it... - if (!v) return TC_ACT_OK; - - uint32_t stat_and_limit_k = skb->ifindex; - - TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k); - - // If we don't have anywhere to put stats, then abort... - if (!stat_v) return TC_ACT_OK; - - uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k); - - // If we don't have a limit, then abort... - if (!limit_v) return TC_ACT_OK; - - // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort... - const int pmtu = v->pmtu; - if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK; - - // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default - // outbound path mtu of 1500 is not necessarily correct, but worst case we simply - // undercount, which is still better then not accounting for this overhead at all. - // Note: this really shouldn't be device/path mtu at all, but rather should be - // derived from this particular connection's mss (ie. from gro segment size). - // This would require a much newer kernel with newer ebpf accessors. - // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header) - uint64_t packets = 1; - uint64_t bytes = skb->len; - if (bytes > pmtu) { - const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12; - const int mss = pmtu - tcp_overhead; - const uint64_t payload = bytes - tcp_overhead; - packets = (payload + mss - 1) / mss; - bytes = tcp_overhead * packets + payload; - } - - // Are we past the limit? If so, then abort... - // Note: will not overflow since u64 is 936 years even at 5Gbps. - // Do not drop here. Offload is just that, whenever we fail to handle - // a packet we let the core stack deal with things. - // (The core stack needs to handle limits correctly anyway, - // since we don't offload all traffic in both directions) - if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK; - - if (!is_ethernet) { - is_ethernet = true; - l2_header_size = sizeof(struct ethhdr); - // Try to inject an ethernet header, and simply return if we fail - if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) { - __sync_fetch_and_add(&stat_v->rxErrors, 1); - return TC_ACT_OK; - } - - // bpf_skb_change_head() invalidates all pointers - reload them - data = (void*)(long)skb->data; - data_end = (void*)(long)skb->data_end; - eth = data; - ip6 = (void*)(eth + 1); - - // I do not believe this can ever happen, but keep the verifier happy... - if (data + l2_header_size + sizeof(*ip6) > data_end) { - __sync_fetch_and_add(&stat_v->rxErrors, 1); - return TC_ACT_SHOT; - } - }; - - // CHECKSUM_COMPLETE is a 16-bit one's complement sum, - // thus corrections for it need to be done in 16-byte chunks at even offsets. - // IPv6 nexthdr is at offset 6, while hop limit is at offset 7 - uint8_t old_hl = ip6->hop_limit; - --ip6->hop_limit; - uint8_t new_hl = ip6->hop_limit; - - // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error - // (-ENOTSUPP) if it isn't. - bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl)); - - __sync_fetch_and_add(&stat_v->rxPackets, packets); - __sync_fetch_and_add(&stat_v->rxBytes, bytes); - - // Overwrite any mac header with the new one - *eth = v->macHeader; - - // Redirect to forwarded interface. - // - // Note that bpf_redirect() cannot fail unless you pass invalid flags. - // The redirect actually happens after the ebpf program has already terminated, - // and can fail for example for mtu reasons at that point in time, but there's nothing - // we can do about it here. - return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */); -} - -SEC("schedcls/ingress/tether_ether") -int sched_cls_ingress_tether_ether(struct __sk_buff* skb) { - return do_forward(skb, true); -} - -// Note: section names must be unique to prevent programs from appending to each other, -// so instead the bpf loader will strip everything past the final $ symbol when actually -// pinning the program into the filesystem. -// -// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed: -// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head -// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu -// (the first of those has already been upstreamed) -// -// 5.4 kernel support was only added to Android Common Kernel in R, -// and thus a 5.4 kernel always supports this. -// -// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels: -DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return do_forward(skb, false); -} - -// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels: -DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0), - KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return do_forward(skb, false); -} - -// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels. -// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned -// it at the same location this one would be pinned at and will thus skip loading this stub) -DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return TC_ACT_OK; -} - -LICENSE("Apache 2.0"); -CRITICAL("netd"); diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp deleted file mode 100644 index bf643cdcecc5..000000000000 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_sdk_library { - name: "framework-tethering", - defaults: ["framework-module-defaults"], - impl_library_visibility: ["//frameworks/base/packages/Tethering:__subpackages__"], - - srcs: [":framework-tethering-srcs"], - - jarjar_rules: "jarjar-rules.txt", - installable: true, - - hostdex: true, // for hiddenapi check - apex_available: ["com.android.tethering"], - permitted_packages: ["android.net"], -} - -filegroup { - name: "framework-tethering-srcs", - srcs: [ - "src/android/net/TetheredClient.aidl", - "src/android/net/TetheredClient.java", - "src/android/net/TetheringManager.java", - "src/android/net/TetheringConstants.java", - "src/android/net/IIntResultListener.aidl", - "src/android/net/ITetheringEventCallback.aidl", - "src/android/net/ITetheringConnector.aidl", - "src/android/net/TetheringCallbackStartedParcel.aidl", - "src/android/net/TetheringConfigurationParcel.aidl", - "src/android/net/TetheringRequestParcel.aidl", - "src/android/net/TetherStatesParcel.aidl", - ], - path: "src" -} diff --git a/packages/Tethering/common/TetheringLib/api/current.txt b/packages/Tethering/common/TetheringLib/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt deleted file mode 100644 index 6ddb122936e7..000000000000 --- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt +++ /dev/null @@ -1,41 +0,0 @@ -// Signature format: 2.0 -package android.net { - - public final class TetheringConstants { - field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; - field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; - field public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; - field public static final String EXTRA_SET_ALARM = "extraSetAlarm"; - } - - public class TetheringManager { - ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>); - method public int getLastTetherError(@NonNull String); - method @NonNull public String[] getTetherableBluetoothRegexs(); - method @NonNull public String[] getTetherableIfaces(); - method @NonNull public String[] getTetherableUsbRegexs(); - method @NonNull public String[] getTetherableWifiRegexs(); - method @NonNull public String[] getTetheredIfaces(); - method @NonNull public String[] getTetheringErroredIfaces(); - method public boolean isTetheringSupported(); - method public boolean isTetheringSupported(@NonNull String); - method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean); - method @Deprecated public int setUsbTethering(boolean); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @Deprecated public int tether(@NonNull String); - method @Deprecated public int untether(@NonNull String); - } - - public static interface TetheringManager.TetheringEventCallback { - method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); - } - - public static class TetheringManager.TetheringInterfaceRegexps { - method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs(); - method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs(); - method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); - } - -} - diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/api/removed.txt b/packages/Tethering/common/TetheringLib/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/api/system-current.txt b/packages/Tethering/common/TetheringLib/api/system-current.txt deleted file mode 100644 index edd1ebb5f751..000000000000 --- a/packages/Tethering/common/TetheringLib/api/system-current.txt +++ /dev/null @@ -1,99 +0,0 @@ -// Signature format: 2.0 -package android.net { - - public final class TetheredClient implements android.os.Parcelable { - ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int); - method public int describeContents(); - method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses(); - method @NonNull public android.net.MacAddress getMacAddress(); - method public int getTetheringType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR; - } - - public static final class TetheredClient.AddressInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.LinkAddress getAddress(); - method @Nullable public String getHostname(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR; - } - - public class TetheringManager { - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); - field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; - field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_ETHERNET = 5; // 0x5 - field public static final int TETHERING_INVALID = -1; // 0xffffffff - field public static final int TETHERING_NCM = 4; // 0x4 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHERING_WIFI_P2P = 3; // 0x3 - field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd - field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 - field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf - field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb - field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 - field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 - field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 - field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 - field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 - field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 - field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 - field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 - field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 - field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 - } - - public static interface TetheringManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); - } - - public static interface TetheringManager.StartTetheringCallback { - method public default void onTetheringFailed(int); - method public default void onTetheringStarted(); - } - - public static interface TetheringManager.TetheringEventCallback { - method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>); - method public default void onError(@NonNull String, int); - method public default void onOffloadStatusChanged(int); - method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheringSupported(boolean); - method public default void onUpstreamChanged(@Nullable android.net.Network); - } - - public static class TetheringManager.TetheringRequest { - method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); - method @Nullable public android.net.LinkAddress getLocalIpv4Address(); - method public boolean getShouldShowEntitlementUi(); - method public int getTetheringType(); - method public boolean isExemptFromEntitlementCheck(); - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); - } - -} - diff --git a/packages/Tethering/common/TetheringLib/api/system-removed.txt b/packages/Tethering/common/TetheringLib/api/system-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/packages/Tethering/common/TetheringLib/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt deleted file mode 100644 index e459fad54993..000000000000 --- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt +++ /dev/null @@ -1 +0,0 @@ -# jarjar rules for the bootclasspath tethering framework library here
\ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl b/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl deleted file mode 100644 index c3d66ee14526..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Listener interface allowing objects to listen to various module event. - * {@hide} - */ -oneway interface IIntResultListener { - void onResult(int resultCode); -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl deleted file mode 100644 index cf094aac2cbe..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; - -import android.net.IIntResultListener; -import android.net.ITetheringEventCallback; -import android.net.TetheringRequestParcel; -import android.os.ResultReceiver; - -/** @hide */ -oneway interface ITetheringConnector { - void tether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void untether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void setUsbTethering(boolean enable, String callerPkg, - String callingAttributionTag, IIntResultListener receiver); - - void startTethering(in TetheringRequestParcel request, String callerPkg, - String callingAttributionTag, IIntResultListener receiver); - - void stopTethering(int type, String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver, - boolean showEntitlementUi, String callerPkg, String callingAttributionTag); - - void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg); - - void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg); - - void isTetheringSupported(String callerPkg, String callingAttributionTag, - IIntResultListener receiver); - - void stopAllTethering(String callerPkg, String callingAttributionTag, - IIntResultListener receiver); -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl deleted file mode 100644 index b4e3ba46791c..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.Network; -import android.net.TetheredClient; -import android.net.TetheringConfigurationParcel; -import android.net.TetheringCallbackStartedParcel; -import android.net.TetherStatesParcel; - -/** - * Callback class for receiving tethering changed events. - * @hide - */ -oneway interface ITetheringEventCallback -{ - /** Called immediately after the callbacks are registered */ - void onCallbackStarted(in TetheringCallbackStartedParcel parcel); - void onCallbackStopped(int errorCode); - void onUpstreamChanged(in Network network); - void onConfigurationChanged(in TetheringConfigurationParcel config); - void onTetherStatesChanged(in TetherStatesParcel states); - void onTetherClientsChanged(in List<TetheredClient> clients); - void onOffloadStatusChanged(int status); -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl deleted file mode 100644 index 3d842b337428..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Status details for tethering downstream interfaces. - * {@hide} - */ -parcelable TetherStatesParcel { - String[] availableList; - String[] tetheredList; - String[] localOnlyList; - String[] erroredIfaceList; - // List of Last error code corresponding to each errored iface in erroredIfaceList. */ - // TODO: Improve this as b/143122247. - int[] lastErrorList; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java deleted file mode 100644 index 0b223f42b954..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; - -/** - * Information on a tethered downstream client. - * @hide - */ -@SystemApi -public final class TetheredClient implements Parcelable { - @NonNull - private final MacAddress mMacAddress; - @NonNull - private final List<AddressInfo> mAddresses; - // TODO: use an @IntDef here - private final int mTetheringType; - - public TetheredClient(@NonNull MacAddress macAddress, - @NonNull Collection<AddressInfo> addresses, int tetheringType) { - mMacAddress = macAddress; - mAddresses = new ArrayList<>(addresses); - mTetheringType = tetheringType; - } - - private TetheredClient(@NonNull Parcel in) { - this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mMacAddress, flags); - dest.writeTypedList(mAddresses); - dest.writeInt(mTetheringType); - } - - /** - * Get the MAC address used to identify the client. - */ - @NonNull - public MacAddress getMacAddress() { - return mMacAddress; - } - - /** - * Get information on the list of addresses that are associated with the client. - */ - @NonNull - public List<AddressInfo> getAddresses() { - return new ArrayList<>(mAddresses); - } - - /** - * Get the type of tethering used by the client. - * @return one of the {@code TetheringManager#TETHERING_*} constants. - */ - public int getTetheringType() { - return mTetheringType; - } - - /** - * Return a new {@link TetheredClient} that has all the attributes of this instance, plus the - * {@link AddressInfo} of the provided {@link TetheredClient}. - * - * <p>Duplicate addresses are removed. - * @hide - */ - public TetheredClient addAddresses(@NonNull TetheredClient other) { - final LinkedHashSet<AddressInfo> newAddresses = new LinkedHashSet<>( - mAddresses.size() + other.mAddresses.size()); - newAddresses.addAll(mAddresses); - newAddresses.addAll(other.mAddresses); - return new TetheredClient(mMacAddress, newAddresses, mTetheringType); - } - - @Override - public int hashCode() { - return Objects.hash(mMacAddress, mAddresses, mTetheringType); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof TetheredClient)) return false; - final TetheredClient other = (TetheredClient) obj; - return mMacAddress.equals(other.mMacAddress) - && mAddresses.equals(other.mAddresses) - && mTetheringType == other.mTetheringType; - } - - /** - * Information on an lease assigned to a tethered client. - */ - public static final class AddressInfo implements Parcelable { - @NonNull - private final LinkAddress mAddress; - @Nullable - private final String mHostname; - - /** @hide */ - public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) { - this.mAddress = address; - this.mHostname = hostname; - } - - private AddressInfo(Parcel in) { - this(in.readParcelable(null), in.readString()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mAddress, flags); - dest.writeString(mHostname); - } - - /** - * Get the link address (including prefix length and lifetime) used by the client. - * - * This may be an IPv4 or IPv6 address. - */ - @NonNull - public LinkAddress getAddress() { - return mAddress; - } - - /** - * Get the hostname that was advertised by the client when obtaining its address, if any. - */ - @Nullable - public String getHostname() { - return mHostname; - } - - /** - * Get the expiration time of the address assigned to the client. - * @hide - */ - public long getExpirationTime() { - return mAddress.getExpirationTime(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public int hashCode() { - return Objects.hash(mAddress, mHostname); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof AddressInfo)) return false; - final AddressInfo other = (AddressInfo) obj; - // Use .equals() for addresses as all changes, including address expiry changes, - // should be included. - return other.mAddress.equals(mAddress) - && Objects.equals(mHostname, other.mHostname); - } - - @NonNull - public static final Creator<AddressInfo> CREATOR = new Creator<AddressInfo>() { - @NonNull - @Override - public AddressInfo createFromParcel(@NonNull Parcel in) { - return new AddressInfo(in); - } - - @NonNull - @Override - public AddressInfo[] newArray(int size) { - return new AddressInfo[size]; - } - }; - - @NonNull - @Override - public String toString() { - return "AddressInfo {" - + mAddress - + (mHostname != null ? ", hostname " + mHostname : "") - + "}"; - } - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - public static final Creator<TetheredClient> CREATOR = new Creator<TetheredClient>() { - @NonNull - @Override - public TetheredClient createFromParcel(@NonNull Parcel in) { - return new TetheredClient(in); - } - - @NonNull - @Override - public TetheredClient[] newArray(int size) { - return new TetheredClient[size]; - } - }; - - @NonNull - @Override - public String toString() { - return "TetheredClient {hwAddr " + mMacAddress - + ", addresses " + mAddresses - + ", tetheringType " + mTetheringType - + "}"; - } -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl deleted file mode 100644 index 253eacbd23e7..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.Network; -import android.net.TetheredClient; -import android.net.TetheringConfigurationParcel; -import android.net.TetherStatesParcel; - -/** - * Initial information reported by tethering upon callback registration. - * @hide - */ -parcelable TetheringCallbackStartedParcel { - boolean tetheringSupported; - Network upstreamNetwork; - TetheringConfigurationParcel config; - TetherStatesParcel states; - List<TetheredClient> tetheredClients; - int offloadStatus; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl deleted file mode 100644 index 89f38132ffad..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Configuration details for tethering. - * @hide - */ -parcelable TetheringConfigurationParcel { - int subId; - String[] tetherableUsbRegexs; - String[] tetherableWifiRegexs; - String[] tetherableBluetoothRegexs; - boolean isDunRequired; - boolean chooseUpstreamAutomatically; - int[] preferredUpstreamIfaceTypes; - String[] legacyDhcpRanges; - String[] defaultIPv4DNS; - boolean enableLegacyDhcpServer; - String[] provisioningApp; - String provisioningAppNoUi; - int provisioningCheckPeriod; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java deleted file mode 100644 index f14def6a3a02..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; - -import android.annotation.SystemApi; -import android.os.ResultReceiver; - -/** - * Collections of constants for internal tethering usage. - * - * <p>These hidden constants are not in TetheringManager as they are not part of the API stubs - * generated for TetheringManager, which prevents the tethering module from linking them at - * build time. - * TODO: investigate changing the tethering build rules so that Tethering can reference hidden - * symbols from framework-tethering even when they are in a non-hidden class. - * @hide - */ -@SystemApi(client = MODULE_LIBRARIES) -public final class TetheringConstants { - /** An explicit private class to avoid exposing constructor.*/ - private TetheringConstants() { } - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Includes the type of tethering to enable if any. - */ - public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; - /** - * Extra used for communicating with the TetherService. Includes the type of tethering for - * which to cancel provisioning. - */ - public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; - /** - * Extra used for communicating with the TetherService. True to schedule a recheck of tether - * provisioning. - */ - public static final String EXTRA_SET_ALARM = "extraSetAlarm"; - /** - * Tells the TetherService to run a provision check now. - */ - public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the {@link ResultReceiver} which will receive provisioning results. - * Can not be empty. - */ - public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the subId of current active cellular upstream. - * @hide - */ - public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; - - /** - * Extra used for telling TetherProvisioningActivity the entitlement package name and class - * name to start UI entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = - "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; - - /** - * Extra used for telling TetherService the intent action to start silent entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = - "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; - - /** - * Extra used for TetherService to receive the response of provisioning check. - * @hide - */ - public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = - "android.net.extra.TETHER_PROVISIONING_RESPONSE"; -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java deleted file mode 100644 index 13b05a841d24..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ /dev/null @@ -1,1364 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; - -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; - -import android.Manifest; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.content.Context; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -/** - * This class provides the APIs to control the tethering service. - * <p> The primary responsibilities of this class are to provide the APIs for applications to - * start tethering, stop tethering, query configuration and query status. - * - * @hide - */ -@SystemApi -public class TetheringManager { - private static final String TAG = TetheringManager.class.getSimpleName(); - private static final int DEFAULT_TIMEOUT_MS = 60_000; - private static final long CONNECTOR_POLL_INTERVAL_MILLIS = 200L; - - @GuardedBy("mConnectorWaitQueue") - @Nullable - private ITetheringConnector mConnector; - @GuardedBy("mConnectorWaitQueue") - @NonNull - private final List<ConnectorConsumer> mConnectorWaitQueue = new ArrayList<>(); - private final Supplier<IBinder> mConnectorSupplier; - - private final TetheringCallbackInternal mCallback; - private final Context mContext; - private final ArrayMap<TetheringEventCallback, ITetheringEventCallback> - mTetheringEventCallbacks = new ArrayMap<>(); - - private volatile TetheringConfigurationParcel mTetheringConfiguration; - private volatile TetherStatesParcel mTetherStatesParcel; - - /** - * Broadcast Action: A tetherable connection has come or gone. - * Uses {@code TetheringManager.EXTRA_AVAILABLE_TETHER}, - * {@code TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY}, - * {@code TetheringManager.EXTRA_ACTIVE_TETHER}, and - * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate - * the current state of tethering. Each include a list of - * interface names in that state (may be empty). - */ - public static final String ACTION_TETHER_STATE_CHANGED = - "android.net.conn.TETHER_STATE_CHANGED"; - - /** - * gives a String[] listing all the interfaces configured for - * tethering and currently available for tethering. - */ - public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - - /** - * gives a String[] listing all the interfaces currently in local-only - * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding) - */ - public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - - /** - * gives a String[] listing all the interfaces currently tethered - * (ie, has DHCPv4 support and packets potentially forwarded/NATed) - */ - public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - - /** - * gives a String[] listing all the interfaces we tried to tether and - * failed. Use {@link #getLastTetherError} to find the error code - * for any interfaces listed here. - */ - public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = false, value = { - TETHERING_WIFI, - TETHERING_USB, - TETHERING_BLUETOOTH, - TETHERING_WIFI_P2P, - TETHERING_NCM, - TETHERING_ETHERNET, - }) - public @interface TetheringType { - } - - /** - * Invalid tethering type. - * @see #startTethering. - */ - public static final int TETHERING_INVALID = -1; - - /** - * Wifi tethering type. - * @see #startTethering. - */ - public static final int TETHERING_WIFI = 0; - - /** - * USB tethering type. - * @see #startTethering. - */ - public static final int TETHERING_USB = 1; - - /** - * Bluetooth tethering type. - * @see #startTethering. - */ - public static final int TETHERING_BLUETOOTH = 2; - - /** - * Wifi P2p tethering type. - * Wifi P2p tethering is set through events automatically, and don't - * need to start from #startTethering. - */ - public static final int TETHERING_WIFI_P2P = 3; - - /** - * Ncm local tethering type. - * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) - */ - public static final int TETHERING_NCM = 4; - - /** - * Ethernet tethering type. - * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) - */ - public static final int TETHERING_ETHERNET = 5; - - /** - * WIGIG tethering type. Use a separate type to prevent - * conflicts with TETHERING_WIFI - * This type is only used internally by the tethering module - * @hide - */ - public static final int TETHERING_WIGIG = 6; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - TETHER_ERROR_NO_ERROR, - TETHER_ERROR_PROVISIONING_FAILED, - TETHER_ERROR_ENTITLEMENT_UNKNOWN, - }) - public @interface EntitlementResult { - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - TETHER_ERROR_NO_ERROR, - TETHER_ERROR_UNKNOWN_IFACE, - TETHER_ERROR_SERVICE_UNAVAIL, - TETHER_ERROR_INTERNAL_ERROR, - TETHER_ERROR_TETHER_IFACE_ERROR, - TETHER_ERROR_ENABLE_FORWARDING_ERROR, - TETHER_ERROR_DISABLE_FORWARDING_ERROR, - TETHER_ERROR_IFACE_CFG_ERROR, - TETHER_ERROR_DHCPSERVER_ERROR, - }) - public @interface TetheringIfaceError { - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - TETHER_ERROR_SERVICE_UNAVAIL, - TETHER_ERROR_INTERNAL_ERROR, - TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, - TETHER_ERROR_UNKNOWN_TYPE, - }) - public @interface StartTetheringError { - } - - public static final int TETHER_ERROR_NO_ERROR = 0; - public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; - public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; - public static final int TETHER_ERROR_UNSUPPORTED = 3; - public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; - public static final int TETHER_ERROR_INTERNAL_ERROR = 5; - public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; - public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; - public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; - public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; - public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; - public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; - public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; - public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; - public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; - public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; - public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = false, value = { - TETHER_HARDWARE_OFFLOAD_STOPPED, - TETHER_HARDWARE_OFFLOAD_STARTED, - TETHER_HARDWARE_OFFLOAD_FAILED, - }) - public @interface TetherOffloadStatus { - } - - /** Tethering offload status is stopped. */ - public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; - /** Tethering offload status is started. */ - public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; - /** Fail to start tethering offload. */ - public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; - - /** - * Create a TetheringManager object for interacting with the tethering service. - * - * @param context Context for the manager. - * @param connectorSupplier Supplier for the manager connector; may return null while the - * service is not connected. - * {@hide} - */ - @SystemApi(client = MODULE_LIBRARIES) - public TetheringManager(@NonNull final Context context, - @NonNull Supplier<IBinder> connectorSupplier) { - mContext = context; - mCallback = new TetheringCallbackInternal(); - mConnectorSupplier = connectorSupplier; - - final String pkgName = mContext.getOpPackageName(); - - final IBinder connector = mConnectorSupplier.get(); - // If the connector is available on start, do not start a polling thread. This introduces - // differences in the thread that sends the oneway binder calls to the service between the - // first few seconds after boot and later, but it avoids always having differences between - // the first usage of TetheringManager from a process and subsequent usages (so the - // difference is only on boot). On boot binder calls may be queued until the service comes - // up and be sent from a worker thread; later, they are always sent from the caller thread. - // Considering that it's just oneway binder calls, and ordering is preserved, this seems - // better than inconsistent behavior persisting after boot. - if (connector != null) { - mConnector = ITetheringConnector.Stub.asInterface(connector); - } else { - startPollingForConnector(); - } - - Log.i(TAG, "registerTetheringEventCallback:" + pkgName); - getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName)); - } - - private void startPollingForConnector() { - new Thread(() -> { - while (true) { - try { - Thread.sleep(CONNECTOR_POLL_INTERVAL_MILLIS); - } catch (InterruptedException e) { - // Not much to do here, the system needs to wait for the connector - } - - final IBinder connector = mConnectorSupplier.get(); - if (connector != null) { - onTetheringConnected(ITetheringConnector.Stub.asInterface(connector)); - return; - } - } - }).start(); - } - - private interface ConnectorConsumer { - void onConnectorAvailable(ITetheringConnector connector) throws RemoteException; - } - - private void onTetheringConnected(ITetheringConnector connector) { - // Process the connector wait queue in order, including any items that are added - // while processing. - // - // 1. Copy the queue to a local variable under lock. - // 2. Drain the local queue with the lock released (otherwise, enqueuing future commands - // would block on the lock). - // 3. Acquire the lock again. If any new tasks were queued during step 2, goto 1. - // If not, set mConnector to non-null so future tasks are run immediately, not queued. - // - // For this to work, all calls to the tethering service must use getConnector(), which - // ensures that tasks are added to the queue with the lock held. - // - // Once mConnector is set to non-null, it will never be null again. If the network stack - // process crashes, no recovery is possible. - // TODO: evaluate whether it is possible to recover from network stack process crashes - // (though in most cases the system will have crashed when the network stack process - // crashes). - do { - final List<ConnectorConsumer> localWaitQueue; - synchronized (mConnectorWaitQueue) { - localWaitQueue = new ArrayList<>(mConnectorWaitQueue); - mConnectorWaitQueue.clear(); - } - - // Allow more tasks to be added at the end without blocking while draining the queue. - for (ConnectorConsumer task : localWaitQueue) { - try { - task.onConnectorAvailable(connector); - } catch (RemoteException e) { - // Most likely the network stack process crashed, which is likely to crash the - // system. Keep processing other requests but report the error loudly. - Log.wtf(TAG, "Error processing request for the tethering connector", e); - } - } - - synchronized (mConnectorWaitQueue) { - if (mConnectorWaitQueue.size() == 0) { - mConnector = connector; - return; - } - } - } while (true); - } - - /** - * Asynchronously get the ITetheringConnector to execute some operation. - * - * <p>If the connector is already available, the operation will be executed on the caller's - * thread. Otherwise it will be queued and executed on a worker thread. The operation should be - * limited to performing oneway binder calls to minimize differences due to threading. - */ - private void getConnector(ConnectorConsumer consumer) { - final ITetheringConnector connector; - synchronized (mConnectorWaitQueue) { - connector = mConnector; - if (connector == null) { - mConnectorWaitQueue.add(consumer); - return; - } - } - - try { - consumer.onConnectorAvailable(connector); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - } - - private interface RequestHelper { - void runRequest(ITetheringConnector connector, IIntResultListener listener); - } - - // Used to dispatch legacy ConnectivityManager methods that expect tethering to be able to - // return results and perform operations synchronously. - // TODO: remove once there are no callers of these legacy methods. - private class RequestDispatcher { - private final ConditionVariable mWaiting; - public volatile int mRemoteResult; - - private final IIntResultListener mListener = new IIntResultListener.Stub() { - @Override - public void onResult(final int resultCode) { - mRemoteResult = resultCode; - mWaiting.open(); - } - }; - - RequestDispatcher() { - mWaiting = new ConditionVariable(); - } - - int waitForResult(final RequestHelper request) { - getConnector(c -> request.runRequest(c, mListener)); - if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) { - throw new IllegalStateException("Callback timeout"); - } - - throwIfPermissionFailure(mRemoteResult); - - return mRemoteResult; - } - } - - private void throwIfPermissionFailure(final int errorCode) { - switch (errorCode) { - case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION: - throw new SecurityException("No android.permission.TETHER_PRIVILEGED" - + " or android.permission.WRITE_SETTINGS permission"); - case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION: - throw new SecurityException( - "No android.permission.ACCESS_NETWORK_STATE permission"); - } - } - - private class TetheringCallbackInternal extends ITetheringEventCallback.Stub { - private volatile int mError = TETHER_ERROR_NO_ERROR; - private final ConditionVariable mWaitForCallback = new ConditionVariable(); - - @Override - public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { - mTetheringConfiguration = parcel.config; - mTetherStatesParcel = parcel.states; - mWaitForCallback.open(); - } - - @Override - public void onCallbackStopped(int errorCode) { - mError = errorCode; - mWaitForCallback.open(); - } - - @Override - public void onUpstreamChanged(Network network) { } - - @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { - mTetheringConfiguration = config; - } - - @Override - public void onTetherStatesChanged(TetherStatesParcel states) { - mTetherStatesParcel = states; - } - - @Override - public void onTetherClientsChanged(List<TetheredClient> clients) { } - - @Override - public void onOffloadStatusChanged(int status) { } - - public void waitForStarted() { - mWaitForCallback.block(DEFAULT_TIMEOUT_MS); - throwIfPermissionFailure(mError); - } - } - - /** - * Attempt to tether the named interface. This will setup a dhcp server - * on the interface, forward and NAT IP v4 packets and forward DNS requests - * to the best active upstream network interface. Note that if no upstream - * IP network interface is available, dhcp will still run and traffic will be - * allowed between the tethered devices and this device, though upstream net - * access will of course fail until an upstream network interface becomes - * active. - * - * @deprecated The only usages is PanService. It uses this for legacy reasons - * and will migrate away as soon as possible. - * - * @param iface the interface name to tether. - * @return error a {@code TETHER_ERROR} value indicating success or failure type - * - * {@hide} - */ - @Deprecated - @SystemApi(client = MODULE_LIBRARIES) - public int tether(@NonNull final String iface) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "tether caller:" + callerPkg); - final RequestDispatcher dispatcher = new RequestDispatcher(); - - return dispatcher.waitForResult((connector, listener) -> { - try { - connector.tether(iface, callerPkg, getAttributionTag(), listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - /** - * @return the context's attribution tag - */ - private @Nullable String getAttributionTag() { - return mContext.getAttributionTag(); - } - - /** - * Stop tethering the named interface. - * - * @deprecated The only usages is PanService. It uses this for legacy reasons - * and will migrate away as soon as possible. - * - * {@hide} - */ - @Deprecated - @SystemApi(client = MODULE_LIBRARIES) - public int untether(@NonNull final String iface) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "untether caller:" + callerPkg); - - final RequestDispatcher dispatcher = new RequestDispatcher(); - - return dispatcher.waitForResult((connector, listener) -> { - try { - connector.untether(iface, callerPkg, getAttributionTag(), listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - /** - * Attempt to both alter the mode of USB and Tethering of USB. - * - * @deprecated New client should not use this API anymore. All clients should use - * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is - * used and an entitlement check is needed, downstream USB tethering will be enabled but will - * not have any upstream. - * - * {@hide} - */ - @Deprecated - @SystemApi(client = MODULE_LIBRARIES) - public int setUsbTethering(final boolean enable) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "setUsbTethering caller:" + callerPkg); - - final RequestDispatcher dispatcher = new RequestDispatcher(); - - return dispatcher.waitForResult((connector, listener) -> { - try { - connector.setUsbTethering(enable, callerPkg, getAttributionTag(), - listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - /** - * Use with {@link #startTethering} to specify additional parameters when starting tethering. - */ - public static class TetheringRequest { - /** A configuration set for TetheringRequest. */ - private final TetheringRequestParcel mRequestParcel; - - private TetheringRequest(final TetheringRequestParcel request) { - mRequestParcel = request; - } - - /** Builder used to create TetheringRequest. */ - public static class Builder { - private final TetheringRequestParcel mBuilderParcel; - - /** Default constructor of Builder. */ - public Builder(@TetheringType final int type) { - mBuilderParcel = new TetheringRequestParcel(); - mBuilderParcel.tetheringType = type; - mBuilderParcel.localIPv4Address = null; - mBuilderParcel.staticClientAddress = null; - mBuilderParcel.exemptFromEntitlementCheck = false; - mBuilderParcel.showProvisioningUi = true; - } - - /** - * Configure tethering with static IPv4 assignment. - * - * A DHCP server will be started, but will only be able to offer the client address. - * The two addresses must be in the same prefix. - * - * @param localIPv4Address The preferred local IPv4 link address to use. - * @param clientAddress The static client address. - */ - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - @NonNull - public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address, - @NonNull final LinkAddress clientAddress) { - Objects.requireNonNull(localIPv4Address); - Objects.requireNonNull(clientAddress); - if (!checkStaticAddressConfiguration(localIPv4Address, clientAddress)) { - throw new IllegalArgumentException("Invalid server or client addresses"); - } - - mBuilderParcel.localIPv4Address = localIPv4Address; - mBuilderParcel.staticClientAddress = clientAddress; - return this; - } - - /** Start tethering without entitlement checks. */ - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - @NonNull - public Builder setExemptFromEntitlementCheck(boolean exempt) { - mBuilderParcel.exemptFromEntitlementCheck = exempt; - return this; - } - - /** - * If an entitlement check is needed, sets whether to show the entitlement UI or to - * perform a silent entitlement check. By default, the entitlement UI is shown. - */ - @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) - @NonNull - public Builder setShouldShowEntitlementUi(boolean showUi) { - mBuilderParcel.showProvisioningUi = showUi; - return this; - } - - /** Build {@link TetheringRequest] with the currently set configuration. */ - @NonNull - public TetheringRequest build() { - return new TetheringRequest(mBuilderParcel); - } - } - - /** - * Get the local IPv4 address, if one was configured with - * {@link Builder#setStaticIpv4Addresses}. - */ - @Nullable - public LinkAddress getLocalIpv4Address() { - return mRequestParcel.localIPv4Address; - } - - /** - * Get the static IPv4 address of the client, if one was configured with - * {@link Builder#setStaticIpv4Addresses}. - */ - @Nullable - public LinkAddress getClientStaticIpv4Address() { - return mRequestParcel.staticClientAddress; - } - - /** Get tethering type. */ - @TetheringType - public int getTetheringType() { - return mRequestParcel.tetheringType; - } - - /** Check if exempt from entitlement check. */ - public boolean isExemptFromEntitlementCheck() { - return mRequestParcel.exemptFromEntitlementCheck; - } - - /** Check if show entitlement ui. */ - public boolean getShouldShowEntitlementUi() { - return mRequestParcel.showProvisioningUi; - } - - /** - * Check whether the two addresses are ipv4 and in the same prefix. - * @hide - */ - public static boolean checkStaticAddressConfiguration( - @NonNull final LinkAddress localIPv4Address, - @NonNull final LinkAddress clientAddress) { - return localIPv4Address.getPrefixLength() == clientAddress.getPrefixLength() - && localIPv4Address.isIpv4() && clientAddress.isIpv4() - && new IpPrefix(localIPv4Address.toString()).equals( - new IpPrefix(clientAddress.toString())); - } - - /** - * Get a TetheringRequestParcel from the configuration - * @hide - */ - public TetheringRequestParcel getParcel() { - return mRequestParcel; - } - - /** String of TetheringRequest detail. */ - public String toString() { - return "TetheringRequest [ type= " + mRequestParcel.tetheringType - + ", localIPv4Address= " + mRequestParcel.localIPv4Address - + ", staticClientAddress= " + mRequestParcel.staticClientAddress - + ", exemptFromEntitlementCheck= " - + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " - + mRequestParcel.showProvisioningUi + " ]"; - } - } - - /** - * Callback for use with {@link #startTethering} to find out whether tethering succeeded. - */ - public interface StartTetheringCallback { - /** - * Called when tethering has been successfully started. - */ - default void onTetheringStarted() {} - - /** - * Called when starting tethering failed. - * - * @param error The error that caused the failure. - */ - default void onTetheringFailed(@StartTetheringError final int error) {} - } - - /** - * Starts tethering and runs tether provisioning for the given type if needed. If provisioning - * fails, stopTethering will be called automatically. - * - * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - * - * @param request a {@link TetheringRequest} which can specify the preferred configuration. - * @param executor {@link Executor} to specify the thread upon which the callback of - * TetheringRequest will be invoked. - * @param callback A callback that will be called to indicate the success status of the - * tethering start request. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void startTethering(@NonNull final TetheringRequest request, - @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "startTethering caller:" + callerPkg); - - final IIntResultListener listener = new IIntResultListener.Stub() { - @Override - public void onResult(final int resultCode) { - executor.execute(() -> { - if (resultCode == TETHER_ERROR_NO_ERROR) { - callback.onTetheringStarted(); - } else { - callback.onTetheringFailed(resultCode); - } - }); - } - }; - getConnector(c -> c.startTethering(request.getParcel(), callerPkg, - getAttributionTag(), listener)); - } - - /** - * Starts tethering and runs tether provisioning for the given type if needed. If provisioning - * fails, stopTethering will be called automatically. - * - * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - * - * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. - * @param executor {@link Executor} to specify the thread upon which the callback of - * TetheringRequest will be invoked. - * @hide - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - @SystemApi(client = MODULE_LIBRARIES) - public void startTethering(int type, @NonNull final Executor executor, - @NonNull final StartTetheringCallback callback) { - startTethering(new TetheringRequest.Builder(type).build(), executor, callback); - } - - /** - * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if - * applicable. - * - * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void stopTethering(@TetheringType final int type) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "stopTethering caller:" + callerPkg); - - getConnector(c -> c.stopTethering(type, callerPkg, getAttributionTag(), - new IIntResultListener.Stub() { - @Override - public void onResult(int resultCode) { - // TODO: provide an API to obtain result - // This has never been possible as stopTethering has always been void and never - // taken a callback object. The only indication that callers have is if the call - // results in a TETHER_STATE_CHANGE broadcast. - } - })); - } - - /** - * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether - * entitlement succeeded. - */ - public interface OnTetheringEntitlementResultListener { - /** - * Called to notify entitlement result. - * - * @param resultCode an int value of entitlement result. It may be one of - * {@link #TETHER_ERROR_NO_ERROR}, - * {@link #TETHER_ERROR_PROVISIONING_FAILED}, or - * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}. - */ - void onTetheringEntitlementResult(@EntitlementResult int result); - } - - /** - * Request the latest value of the tethering entitlement check. - * - * <p>This method will only return the latest entitlement result if it is available. If no - * cached entitlement result is available, and {@code showEntitlementUi} is false, - * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is - * true, entitlement will be run. - * - * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - * - * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants. - * @param showEntitlementUi a boolean indicating whether to check result for the UI-based - * entitlement check or the silent entitlement check. - * @param executor the executor on which callback will be invoked. - * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to - * notify the caller of the result of entitlement check. The listener may be called zero - * or one time. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void requestLatestTetheringEntitlementResult(@TetheringType int type, - boolean showEntitlementUi, - @NonNull Executor executor, - @NonNull final OnTetheringEntitlementResultListener listener) { - if (listener == null) { - throw new IllegalArgumentException( - "OnTetheringEntitlementResultListener cannot be null."); - } - - ResultReceiver wrappedListener = new ResultReceiver(null /* handler */) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - executor.execute(() -> { - listener.onTetheringEntitlementResult(resultCode); - }); - } - }; - - requestLatestTetheringEntitlementResult(type, wrappedListener, - showEntitlementUi); - } - - /** - * Helper function of #requestLatestTetheringEntitlementResult to remain backwards compatible - * with ConnectivityManager#getLatestTetheringEntitlementResult - * - * {@hide} - */ - // TODO: improve the usage of ResultReceiver, b/145096122 - @SystemApi(client = MODULE_LIBRARIES) - public void requestLatestTetheringEntitlementResult(@TetheringType final int type, - @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg); - - getConnector(c -> c.requestLatestTetheringEntitlementResult( - type, receiver, showEntitlementUi, callerPkg, getAttributionTag())); - } - - /** - * Callback for use with {@link registerTetheringEventCallback} to find out tethering - * upstream status. - */ - public interface TetheringEventCallback { - /** - * Called when tethering supported status changed. - * - * <p>This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * - * <p>Tethering may be disabled via system properties, device configuration, or device - * policy restrictions. - * - * @param supported The new supported status - */ - default void onTetheringSupported(boolean supported) {} - - /** - * Called when tethering upstream changed. - * - * <p>This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * - * @param network the {@link Network} of tethering upstream. Null means tethering doesn't - * have any upstream. - */ - default void onUpstreamChanged(@Nullable Network network) {} - - /** - * Called when there was a change in tethering interface regular expressions. - * - * <p>This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * @param reg The new regular expressions. - * - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {} - - /** - * Called when there was a change in the list of tetherable interfaces. Tetherable - * interface means this interface is available and can be used for tethering. - * - * <p>This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * @param interfaces The list of tetherable interface names. - */ - default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {} - - /** - * Called when there was a change in the list of tethered interfaces. - * - * <p>This will be called immediately after the callback is registered, and may be called - * multiple times later upon changes. - * @param interfaces The list of 0 or more String of currently tethered interface names. - */ - default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {} - - /** - * Called when an error occurred configuring tethering. - * - * <p>This will be called immediately after the callback is registered if the latest status - * on the interface is an error, and may be called multiple times later upon changes. - * @param ifName Name of the interface. - * @param error One of {@code TetheringManager#TETHER_ERROR_*}. - */ - default void onError(@NonNull String ifName, @TetheringIfaceError int error) {} - - /** - * Called when the list of tethered clients changes. - * - * <p>This callback provides best-effort information on connected clients based on state - * known to the system, however the list cannot be completely accurate (and should not be - * used for security purposes). For example, clients behind a bridge and using static IP - * assignments are not visible to the tethering device; or even when using DHCP, such - * clients may still be reported by this callback after disconnection as the system cannot - * determine if they are still connected. - * @param clients The new set of tethered clients; the collection is not ordered. - */ - default void onClientsChanged(@NonNull Collection<TetheredClient> clients) {} - - /** - * Called when tethering offload status changes. - * - * <p>This will be called immediately after the callback is registered. - * @param status The offload status. - */ - default void onOffloadStatusChanged(@TetherOffloadStatus int status) {} - } - - /** - * Regular expressions used to identify tethering interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public static class TetheringInterfaceRegexps { - private final String[] mTetherableBluetoothRegexs; - private final String[] mTetherableUsbRegexs; - private final String[] mTetherableWifiRegexs; - - /** @hide */ - public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs, - @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) { - mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone(); - mTetherableUsbRegexs = tetherableUsbRegexs.clone(); - mTetherableWifiRegexs = tetherableWifiRegexs.clone(); - } - - @NonNull - public List<String> getTetherableBluetoothRegexs() { - return Collections.unmodifiableList(Arrays.asList(mTetherableBluetoothRegexs)); - } - - @NonNull - public List<String> getTetherableUsbRegexs() { - return Collections.unmodifiableList(Arrays.asList(mTetherableUsbRegexs)); - } - - @NonNull - public List<String> getTetherableWifiRegexs() { - return Collections.unmodifiableList(Arrays.asList(mTetherableWifiRegexs)); - } - - @Override - public int hashCode() { - return Objects.hash(mTetherableBluetoothRegexs, mTetherableUsbRegexs, - mTetherableWifiRegexs); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof TetheringInterfaceRegexps)) return false; - final TetheringInterfaceRegexps other = (TetheringInterfaceRegexps) obj; - return Arrays.equals(mTetherableBluetoothRegexs, other.mTetherableBluetoothRegexs) - && Arrays.equals(mTetherableUsbRegexs, other.mTetherableUsbRegexs) - && Arrays.equals(mTetherableWifiRegexs, other.mTetherableWifiRegexs); - } - } - - /** - * Start listening to tethering change events. Any new added callback will receive the last - * tethering status right away. If callback is registered, - * {@link TetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering - * has no upstream or disabled, the argument of callback will be null. The same callback object - * cannot be registered twice. - * - * @param executor the executor on which callback will be invoked. - * @param callback the callback to be called when tethering has change events. - */ - @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) - public void registerTetheringEventCallback(@NonNull Executor executor, - @NonNull TetheringEventCallback callback) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg); - - synchronized (mTetheringEventCallbacks) { - if (mTetheringEventCallbacks.containsKey(callback)) { - throw new IllegalArgumentException("callback was already registered."); - } - final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() { - // Only accessed with a lock on this object - private final HashMap<String, Integer> mErrorStates = new HashMap<>(); - private String[] mLastTetherableInterfaces = null; - private String[] mLastTetheredInterfaces = null; - - @Override - public void onUpstreamChanged(Network network) throws RemoteException { - executor.execute(() -> { - callback.onUpstreamChanged(network); - }); - } - - private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) { - for (int i = 0; i < newStates.erroredIfaceList.length; i++) { - final String iface = newStates.erroredIfaceList[i]; - final Integer lastError = mErrorStates.get(iface); - final int newError = newStates.lastErrorList[i]; - if (newError != TETHER_ERROR_NO_ERROR - && !Objects.equals(lastError, newError)) { - callback.onError(iface, newError); - } - mErrorStates.put(iface, newError); - } - } - - private synchronized void maybeSendTetherableIfacesChangedCallback( - final TetherStatesParcel newStates) { - if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return; - mLastTetherableInterfaces = newStates.availableList.clone(); - callback.onTetherableInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces))); - } - - private synchronized void maybeSendTetheredIfacesChangedCallback( - final TetherStatesParcel newStates) { - if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return; - mLastTetheredInterfaces = newStates.tetheredList.clone(); - callback.onTetheredInterfacesChanged( - Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces))); - } - - // Called immediately after the callbacks are registered. - @Override - public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { - executor.execute(() -> { - callback.onTetheringSupported(parcel.tetheringSupported); - callback.onUpstreamChanged(parcel.upstreamNetwork); - sendErrorCallbacks(parcel.states); - sendRegexpsChanged(parcel.config); - maybeSendTetherableIfacesChangedCallback(parcel.states); - maybeSendTetheredIfacesChangedCallback(parcel.states); - callback.onClientsChanged(parcel.tetheredClients); - callback.onOffloadStatusChanged(parcel.offloadStatus); - }); - } - - @Override - public void onCallbackStopped(int errorCode) { - executor.execute(() -> { - throwIfPermissionFailure(errorCode); - }); - } - - private void sendRegexpsChanged(TetheringConfigurationParcel parcel) { - callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps( - parcel.tetherableBluetoothRegexs, - parcel.tetherableUsbRegexs, - parcel.tetherableWifiRegexs)); - } - - @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { - executor.execute(() -> sendRegexpsChanged(config)); - } - - @Override - public void onTetherStatesChanged(TetherStatesParcel states) { - executor.execute(() -> { - sendErrorCallbacks(states); - maybeSendTetherableIfacesChangedCallback(states); - maybeSendTetheredIfacesChangedCallback(states); - }); - } - - @Override - public void onTetherClientsChanged(final List<TetheredClient> clients) { - executor.execute(() -> callback.onClientsChanged(clients)); - } - - @Override - public void onOffloadStatusChanged(final int status) { - executor.execute(() -> callback.onOffloadStatusChanged(status)); - } - }; - getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg)); - mTetheringEventCallbacks.put(callback, remoteCallback); - } - } - - /** - * Remove tethering event callback previously registered with - * {@link #registerTetheringEventCallback}. - * - * @param callback previously registered callback. - */ - @RequiresPermission(anyOf = { - Manifest.permission.TETHER_PRIVILEGED, - Manifest.permission.ACCESS_NETWORK_STATE - }) - public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg); - - synchronized (mTetheringEventCallbacks) { - ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback); - if (remoteCallback == null) { - throw new IllegalArgumentException("callback was not registered."); - } - - getConnector(c -> c.unregisterTetheringEventCallback(remoteCallback, callerPkg)); - } - } - - /** - * Get a more detailed error code after a Tethering or Untethering - * request asynchronously failed. - * - * @param iface The name of the interface of interest - * @return error The error code of the last error tethering or untethering the named - * interface - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public int getLastTetherError(@NonNull final String iface) { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR; - - int i = 0; - for (String errored : mTetherStatesParcel.erroredIfaceList) { - if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i]; - - i++; - } - return TETHER_ERROR_NO_ERROR; - } - - /** - * Get the list of regular expressions that define any tetherable - * USB network interfaces. If USB tethering is not supported by the - * device, this list should be empty. - * - * @return an array of 0 or more regular expression Strings defining - * what interfaces are considered tetherable usb interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableUsbRegexs() { - mCallback.waitForStarted(); - return mTetheringConfiguration.tetherableUsbRegexs; - } - - /** - * Get the list of regular expressions that define any tetherable - * Wifi network interfaces. If Wifi tethering is not supported by the - * device, this list should be empty. - * - * @return an array of 0 or more regular expression Strings defining - * what interfaces are considered tetherable wifi interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableWifiRegexs() { - mCallback.waitForStarted(); - return mTetheringConfiguration.tetherableWifiRegexs; - } - - /** - * Get the list of regular expressions that define any tetherable - * Bluetooth network interfaces. If Bluetooth tethering is not supported by the - * device, this list should be empty. - * - * @return an array of 0 or more regular expression Strings defining - * what interfaces are considered tetherable bluetooth interfaces. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableBluetoothRegexs() { - mCallback.waitForStarted(); - return mTetheringConfiguration.tetherableBluetoothRegexs; - } - - /** - * Get the set of tetherable, available interfaces. This list is limited by - * device configuration and current interface existence. - * - * @return an array of 0 or more Strings of tetherable interface names. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetherableIfaces() { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return new String[0]; - - return mTetherStatesParcel.availableList; - } - - /** - * Get the set of tethered interfaces. - * - * @return an array of 0 or more String of currently tethered interface names. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetheredIfaces() { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return new String[0]; - - return mTetherStatesParcel.tetheredList; - } - - /** - * Get the set of interface names which attempted to tether but - * failed. Re-attempting to tether may cause them to reset to the Tethered - * state. Alternatively, causing the interface to be destroyed and recreated - * may cause them to reset to the available state. - * {@link TetheringManager#getLastTetherError} can be used to get more - * information on the cause of the errors. - * - * @return an array of 0 or more String indicating the interface names - * which failed to tether. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public @NonNull String[] getTetheringErroredIfaces() { - mCallback.waitForStarted(); - if (mTetherStatesParcel == null) return new String[0]; - - return mTetherStatesParcel.erroredIfaceList; - } - - /** - * Get the set of tethered dhcp ranges. - * - * @deprecated This API just return the default value which is not used in DhcpServer. - * @hide - */ - @Deprecated - public @NonNull String[] getTetheredDhcpRanges() { - mCallback.waitForStarted(); - return mTetheringConfiguration.legacyDhcpRanges; - } - - /** - * Check if the device allows for tethering. It may be disabled via - * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or - * due to device configuration. - * - * @return a boolean - {@code true} indicating Tethering is supported. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public boolean isTetheringSupported() { - final String callerPkg = mContext.getOpPackageName(); - - return isTetheringSupported(callerPkg); - } - - /** - * Check if the device allows for tethering. It may be disabled via {@code ro.tether.denied} - * system property, Settings.TETHER_SUPPORTED or due to device configuration. This is useful - * for system components that query this API on behalf of an app. In particular, Bluetooth - * has @UnsupportedAppUsage calls that will let apps turn on bluetooth tethering if they have - * the right permissions, but such an app needs to know whether it can (permissions as well - * as support from the device) turn on tethering in the first place to show the appropriate UI. - * - * @param callerPkg The caller package name, if it is not matching the calling uid, - * SecurityException would be thrown. - * @return a boolean - {@code true} indicating Tethering is supported. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public boolean isTetheringSupported(@NonNull final String callerPkg) { - - final RequestDispatcher dispatcher = new RequestDispatcher(); - final int ret = dispatcher.waitForResult((connector, listener) -> { - try { - connector.isTetheringSupported(callerPkg, getAttributionTag(), listener); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - - return ret == TETHER_ERROR_NO_ERROR; - } - - /** - * Stop all active tethering. - * - * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will - * fail if a tethering entitlement check is required. - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.TETHER_PRIVILEGED, - android.Manifest.permission.WRITE_SETTINGS - }) - public void stopAllTethering() { - final String callerPkg = mContext.getOpPackageName(); - Log.i(TAG, "stopAllTethering caller:" + callerPkg); - - getConnector(c -> c.stopAllTethering(callerPkg, getAttributionTag(), - new IIntResultListener.Stub() { - @Override - public void onResult(int resultCode) { - // TODO: add an API parameter to send result to caller. - // This has never been possible as stopAllTethering has always been void - // and never taken a callback object. The only indication that callers have - // is if the call results in a TETHER_STATE_CHANGE broadcast. - } - })); - } -} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl deleted file mode 100644 index c0280d3dbfaf..000000000000 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.LinkAddress; - -/** - * Configuration details for requesting tethering. - * @hide - */ -parcelable TetheringRequestParcel { - int tetheringType; - LinkAddress localIPv4Address; - LinkAddress staticClientAddress; - boolean exemptFromEntitlementCheck; - boolean showProvisioningUi; -} diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt deleted file mode 100644 index 591861f5b837..000000000000 --- a/packages/Tethering/jarjar-rules.txt +++ /dev/null @@ -1,11 +0,0 @@ -# These must be kept in sync with the framework-tethering-shared-srcs filegroup. -# Classes from the framework-tethering-shared-srcs filegroup. -# If there are files in that filegroup that are not covered below, the classes in the -# module will be overwritten by the ones in the framework. -rule com.android.internal.util.** com.android.networkstack.tethering.util.@1 -rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 - -rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 - -# Classes from net-utils-framework-common -rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1
\ No newline at end of file diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp deleted file mode 100644 index 94c871d8a366..000000000000 --- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <errno.h> -#include <error.h> -#include <jni.h> -#include <linux/filter.h> -#include <nativehelper/JNIHelp.h> -#include <nativehelper/JNIHelpCompat.h> -#include <nativehelper/ScopedUtfChars.h> -#include <net/if.h> -#include <netinet/ether.h> -#include <netinet/ip6.h> -#include <netinet/icmp6.h> -#include <sys/socket.h> -#include <stdio.h> - -#define LOG_TAG "TetheringUtils" -#include <android/log.h> - -namespace android { - -static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt); -static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr); -static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); - -static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) { - sock_filter filter_code[] = { - // Check header is ICMPv6. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), - - // Check ICMPv6 type. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1), - - // Accept or reject. - BPF_STMT(BPF_RET | BPF_K, 0xffff), - BPF_STMT(BPF_RET | BPF_K, 0) - }; - - const sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code, - }; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); - } -} - -static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd) -{ - android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT); -} - -static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd) -{ - android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT); -} - -static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, - jint ifIndex) -{ - static const int kLinkLocalHopLimit = 255; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - - // Set an ICMPv6 filter that only passes Router Solicitations. - struct icmp6_filter rs_only; - ICMP6_FILTER_SETBLOCKALL(&rs_only); - ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only); - socklen_t len = sizeof(rs_only); - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(ICMP6_FILTER): %s", strerror(errno)); - return; - } - - // Most/all of the rest of these options can be set via Java code, but - // because we're here on account of setting an icmp6_filter go ahead - // and do it all natively for now. - - // Set the multicast hoplimit to 255 (link-local only). - int hops = kLinkLocalHopLimit; - len = sizeof(hops); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); - return; - } - - // Set the unicast hoplimit to 255 (link-local only). - hops = kLinkLocalHopLimit; - len = sizeof(hops); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); - return; - } - - // Explicitly disable multicast loopback. - int off = 0; - len = sizeof(off); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); - return; - } - - // Specify the IPv6 interface to use for outbound multicast. - len = sizeof(ifIndex); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno)); - return; - } - - // Additional options to be considered: - // - IPV6_TCLASS - // - IPV6_RECVPKTINFO - // - IPV6_RECVHOPLIMIT - - // Bind to [::]. - const struct sockaddr_in6 sin6 = { - .sin6_family = AF_INET6, - .sin6_port = 0, - .sin6_flowinfo = 0, - .sin6_addr = IN6ADDR_ANY_INIT, - .sin6_scope_id = 0, - }; - auto sa = reinterpret_cast<const struct sockaddr *>(&sin6); - len = sizeof(sin6); - if (bind(fd, sa, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "bind(IN6ADDR_ANY): %s", strerror(errno)); - return; - } - - // Join the all-routers multicast group, ff02::2%index. - struct ipv6_mreq all_rtrs = { - .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}}, - .ipv6mr_interface = ifIndex, - }; - len = sizeof(all_rtrs); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno)); - return; - } -} - -/* - * JNI registration. - */ -static const JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - { "setupNaSocket", "(Ljava/io/FileDescriptor;)V", - (void*) android_net_util_setupNaSocket }, - { "setupNsSocket", "(Ljava/io/FileDescriptor;)V", - (void*) android_net_util_setupNsSocket }, - { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", - (void*) android_net_util_setupRaSocket }, -}; - -int register_android_net_util_TetheringUtils(JNIEnv* env) { - return jniRegisterNativeMethods(env, - "android/net/util/TetheringUtils", - gMethods, NELEM(gMethods)); -} - -extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { - JNIEnv *env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed"); - return JNI_ERR; - } - - if (register_android_net_util_TetheringUtils(env) < 0) { - return JNI_ERR; - } - - return JNI_VERSION_1_6; -} - -}; // namespace android diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags deleted file mode 100644 index 86b903353cf5..000000000000 --- a/packages/Tethering/proguard.flags +++ /dev/null @@ -1,9 +0,0 @@ -# Keep class's integer static field for MessageUtils to parsing their name. --keep class com.android.networkstack.tethering.Tethering$TetherMainSM { - static final int CMD_*; - static final int EVENT_*; -} - --keepclassmembers class android.net.ip.IpServer { - static final int CMD_*; -} diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png Binary files differdeleted file mode 100644 index 9451174d65d7..000000000000 --- a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png Binary files differdeleted file mode 100644 index 79d5756ae38e..000000000000 --- a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png Binary files differdeleted file mode 100644 index cae1bd1b2574..000000000000 --- a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png Binary files differdeleted file mode 100644 index ffe8e8c98232..000000000000 --- a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png Binary files differdeleted file mode 100644 index ca20f7352090..000000000000 --- a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png Binary files differdeleted file mode 100644 index 65e907565ec1..000000000000 --- a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png Binary files differdeleted file mode 100644 index f42dae0fdcb9..000000000000 --- a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png Binary files differdeleted file mode 100644 index 065516185ad4..000000000000 --- a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png Binary files differdeleted file mode 100644 index 2e2b8ca2e9cb..000000000000 --- a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png Binary files differdeleted file mode 100644 index 3f57d1c76ccb..000000000000 --- a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png Binary files differdeleted file mode 100644 index 34b0cb36736a..000000000000 --- a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png Binary files differdeleted file mode 100644 index 36afe485b5bb..000000000000 --- a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png Binary files differdeleted file mode 100644 index 25acfbb01ba4..000000000000 --- a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png Binary files differdeleted file mode 100644 index 5c656012e615..000000000000 --- a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png +++ /dev/null diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png Binary files differdeleted file mode 100644 index 28b4b5438e55..000000000000 --- a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png +++ /dev/null diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml deleted file mode 100644 index 056168b12e89..000000000000 --- a/packages/Tethering/res/values-af/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Verbinding of warmkol is aktief"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tik om op te stel."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Verbinding is gedeaktiveer"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontak jou administrateur vir besonderhede"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Warmkol- en verbindingstatus"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml deleted file mode 100644 index ac468dd14414..000000000000 --- a/packages/Tethering/res/values-am/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ለማዋቀር መታ ያድርጉ።"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"እንደ ሞደም መሰካት ተሰናክሏል"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml deleted file mode 100644 index 7d5bad34da04..000000000000 --- a/packages/Tethering/res/values-ar/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"النطاق نشط أو نقطة الاتصال نشطة"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"انقر للإعداد."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"التوصيل متوقف."</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"تواصَل مع المشرف للحصول على التفاصيل."</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"حالة نقطة الاتصال والتوصيل"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml deleted file mode 100644 index 091350455ba0..000000000000 --- a/packages/Tethering/res/values-as/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ছেট আপ কৰিবলৈ টিপক।"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml deleted file mode 100644 index dce70da178f1..000000000000 --- a/packages/Tethering/res/values-az/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Birləşmə və ya hotspot aktivdir"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamaq üçün toxunun."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Birləşmə deaktivdir"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Detallar üçün adminlə əlaqə saxlayın"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot & birləşmə statusu"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml deleted file mode 100644 index b0774ec9a840..000000000000 --- a/packages/Tethering/res/values-b+sr+Latn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Privezivanje ili hotspot je aktivan"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste podesili."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Privezivanje je onemogućeno"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Potražite detalje od administratora"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspota i privezivanja"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml deleted file mode 100644 index a8acebe2e992..000000000000 --- a/packages/Tethering/res/values-be/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Мадэм або хот-спот актыўныя"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Дакраніцеся, каб наладзіць."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Рэжым мадэма выключаны"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Стан \"Хот-спот і мадэм\""</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml deleted file mode 100644 index 94fb2d8f176a..000000000000 --- a/packages/Tethering/res/values-bg/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Има активна споделена връзка или точка за достъп"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Докоснете, за да настроите."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Функцията за тетъринг е деактивирана"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Свържете се с администратора си за подробности"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Състояние на функцията за точка за достъп и тетъринг"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml deleted file mode 100644 index aea02b9ddff8..000000000000 --- a/packages/Tethering/res/values-bn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"টিথারিং বা হটস্পট চালু আছে"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"সেট-আপ করতে ট্যাপ করুন।"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"টিথারিং বন্ধ করা আছে"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হটস্পট ও টিথারিং স্ট্যাটাস"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml deleted file mode 100644 index de232724c5d9..000000000000 --- a/packages/Tethering/res/values-bs/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Aktivno je povezivanje putem mobitela ili pristupna tačka"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da postavite."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezivanje putem mobitela je onemogućeno"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontaktirajte svog administratora za detalje"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status pristupne tačke i povezivanja putem mobitela"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml deleted file mode 100644 index 88b795c1f8b2..000000000000 --- a/packages/Tethering/res/values-ca/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Compartició de xarxa o punt d\'accés Wi‑Fi actius"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Toca per configurar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"La compartició de xarxa està desactivada"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta amb el teu administrador per obtenir més informació"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml deleted file mode 100644 index 8c1b83bf3ee3..000000000000 --- a/packages/Tethering/res/values-cs/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering nebo hotspot je aktivní"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím zahájíte nastavení."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je zakázán"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požádejte administrátora"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml deleted file mode 100644 index f413e7054819..000000000000 --- a/packages/Tethering/res/values-da/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Netdeling eller hotspot er aktivt"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tryk for at konfigurere."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Netdeling er deaktiveret"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakt din administrator for at få oplysninger"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for hotspot og netdeling"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml deleted file mode 100644 index f057d7824e81..000000000000 --- a/packages/Tethering/res/values-de/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering oder Hotspot aktiv"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Zum Einrichten tippen."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering ist deaktiviert"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Bitte wende dich für weitere Informationen an den Administrator"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot- und Tethering-Status"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml deleted file mode 100644 index b3c986bdafd6..000000000000 --- a/packages/Tethering/res/values-el/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Πατήστε για ρύθμιση."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Η σύνδεση είναι απενεργοποιημένη"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml deleted file mode 100644 index 769e01208afc..000000000000 --- a/packages/Tethering/res/values-en-rAU/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml deleted file mode 100644 index 769e01208afc..000000000000 --- a/packages/Tethering/res/values-en-rCA/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml deleted file mode 100644 index 769e01208afc..000000000000 --- a/packages/Tethering/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml deleted file mode 100644 index 769e01208afc..000000000000 --- a/packages/Tethering/res/values-en-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml deleted file mode 100644 index f1674bed4eb7..000000000000 --- a/packages/Tethering/res/values-en-rXC/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot & tethering status"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml deleted file mode 100644 index 63689f43997c..000000000000 --- a/packages/Tethering/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión a red o hotspot conectados"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Presiona para configurar esta opción."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Se inhabilitó la conexión mediante dispositivo portátil"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Para obtener más información, comunícate con el administrador"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del hotspot y la conexión mediante dispositivo portátil"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml deleted file mode 100644 index 9a34ed5e388a..000000000000 --- a/packages/Tethering/res/values-es/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida o punto de acceso activos"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"La conexión compartida está inhabilitada"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Solicita más información a tu administrador"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del punto de acceso y de la conexión compartida"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml deleted file mode 100644 index 0970341ab0cd..000000000000 --- a/packages/Tethering/res/values-et/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Jagamine või kuumkoht on aktiivne"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Puudutage seadistamiseks."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Jagamine on keelatud"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Kuumkoha ja jagamise olek"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml deleted file mode 100644 index 632019e2ef1b..000000000000 --- a/packages/Tethering/res/values-eu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Konexioa partekatzea edo wifi-gunea aktibo dago"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Sakatu konfiguratzeko."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Desgaituta dago konexioa partekatzeko aukera"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml deleted file mode 100644 index 2e21c85fa179..000000000000 --- a/packages/Tethering/res/values-fa/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"اشتراکگذاری اینترنت یا نقطه اتصال فعال"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"برای راهاندازی ضربه بزنید."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"اشتراکگذاری اینترنت غیرفعال است"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"برای جزئیات، با سرپرستتان تماس بگیرید"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"وضعیت نقطه اتصال و اشتراکگذاری اینترنت"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml deleted file mode 100644 index 413db3f0f8c9..000000000000 --- a/packages/Tethering/res/values-fi/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Yhteyden jakaminen tai hotspot käytössä"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Ota käyttöön napauttamalla."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Yhteyden jakaminen on poistettu käytöstä"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pyydä lisätietoja järjestelmänvalvojalta"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspotin ja yhteyden jakamisen tila"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml deleted file mode 100644 index eb2e4ba54000..000000000000 --- a/packages/Tethering/res/values-fr-rCA/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès sans fil activé"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Touchez pour configurer."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Communiquez avec votre administrateur pour obtenir plus de détails"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Point d\'accès et partage de connexion"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml deleted file mode 100644 index 22259c52ab9e..000000000000 --- a/packages/Tethering/res/values-fr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès activé"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Appuyez pour effectuer la configuration."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pour en savoir plus, contactez votre administrateur"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"État du point d\'accès et du partage de connexion"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml deleted file mode 100644 index ded82fcd54ad..000000000000 --- a/packages/Tethering/res/values-gl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida ou zona wifi activada"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"A conexión compartida está desactivada"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta co administrador para obter información"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona wifi e da conexión compartida"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml deleted file mode 100644 index 7cbbc2de3d91..000000000000 --- a/packages/Tethering/res/values-gu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"સેટઅપ કરવા માટે ટૅપ કરો."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml deleted file mode 100644 index 08af81b826b3..000000000000 --- a/packages/Tethering/res/values-hi/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग या हॉटस्पॉट चालू है"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"सेट अप करने के लिए टैप करें."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद है"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट और टेदरिंग की स्थिति"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml deleted file mode 100644 index 827c135f205d..000000000000 --- a/packages/Tethering/res/values-hr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Modemsko povezivanje ili žarišna točka aktivni"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste postavili."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modemsko je povezivanje onemogućeno"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Obratite se administratoru da biste saznali pojedinosti"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status žarišne točke i modemskog povezivanja"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml deleted file mode 100644 index eb68d6babf8f..000000000000 --- a/packages/Tethering/res/values-hu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Megosztás vagy aktív hotspot"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Koppintson a beállításhoz."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Az internetmegosztás le van tiltva"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"A részletekért forduljon rendszergazdájához"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot és internetmegosztás állapota"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml deleted file mode 100644 index 912941e53835..000000000000 --- a/packages/Tethering/res/values-hy/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Մոդեմի ռեժիմը միացված է"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Հպեք՝ կարգավորելու համար։"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Մոդեմի ռեժիմն անջատված է"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml deleted file mode 100644 index a4e175a439e9..000000000000 --- a/packages/Tethering/res/values-in/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering atau hotspot aktif"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Ketuk untuk menyiapkan."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering dinonaktifkan"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi admin untuk mengetahui detailnya"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspot & tethering"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml deleted file mode 100644 index e9f6670bcd09..000000000000 --- a/packages/Tethering/res/values-is/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Kveikt á tjóðrun eða aðgangsstað"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Ýttu til að setja upp."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Slökkt er á tjóðrun"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Staða heits reits og tjóðrunar"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml deleted file mode 100644 index ffb9196f5ee3..000000000000 --- a/packages/Tethering/res/values-it/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Hotspot o tethering attivo"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tocca per impostare."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering disattivato"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contatta il tuo amministratore per avere informazioni dettagliate"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stato hotspot e tethering"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml deleted file mode 100644 index 7adcb47350c1..000000000000 --- a/packages/Tethering/res/values-iw/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"יש להקיש כדי להגדיר."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"שיתוף האינטרנט בין מכשירים מושבת"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"לפרטים, יש לפנות למנהל המערכת"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml deleted file mode 100644 index f68a73010b36..000000000000 --- a/packages/Tethering/res/values-ja/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"テザリングまたはアクセス ポイントが有効です"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"タップしてセットアップします。"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"テザリングは無効に設定されています"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳しくは、管理者にお問い合わせください"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"アクセス ポイントとテザリングのステータス"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml deleted file mode 100644 index 7c22e82bd370..000000000000 --- a/packages/Tethering/res/values-ka/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"შეეხეთ დასაყენებლად."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ტეტერინგი გათიშულია"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"უსადენო ქსელის და ტეტერინგის სტატუსი"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml deleted file mode 100644 index 0857d06de243..000000000000 --- a/packages/Tethering/res/values-kk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Тетеринг немесе хотспот қосулы"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Реттеу үшін түртіңіз."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Тетеринг өшірілді."</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Мәліметтерді әкімшіден алыңыз."</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Хотспот және тетеринг күйі"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml deleted file mode 100644 index 536e3d1703b1..000000000000 --- a/packages/Tethering/res/values-km/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ការភ្ជាប់ ឬហតស្ប៉តកំពុងដំណើរការ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ចុចដើម្បីរៀបចំ។"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ការភ្ជាប់ត្រូវបានបិទ"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ទាក់ទងអ្នកគ្រប់គ្រងរបស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml deleted file mode 100644 index 32f54926f4bf..000000000000 --- a/packages/Tethering/res/values-kn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ಹಾಟ್ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್ ಸ್ಥಿತಿ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml deleted file mode 100644 index 156b24786db5..000000000000 --- a/packages/Tethering/res/values-ko/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"테더링 또는 핫스팟 사용"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"설정하려면 탭하세요."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"테더링이 사용 중지됨"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"자세한 정보는 관리자에게 문의하세요."</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"핫스팟 및 테더링 상태"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml deleted file mode 100644 index 18ee5fd35718..000000000000 --- a/packages/Tethering/res/values-ky/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Модем режими күйүп турат"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Жөндөө үчүн таптап коюңуз."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Телефонду модем катары колдонууга болбойт"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Байланыш түйүнүнүн жана модем режиминин статусу"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml deleted file mode 100644 index b12767018c3a..000000000000 --- a/packages/Tethering/res/values-lo/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ແຕະເພື່ອຕັ້ງຄ່າ."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml deleted file mode 100644 index 8427baf39f31..000000000000 --- a/packages/Tethering/res/values-lt/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Palieskite, kad nustatytumėte."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Įrenginio kaip modemo naudojimas išjungtas"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml deleted file mode 100644 index aa2d6990e04f..000000000000 --- a/packages/Tethering/res/values-lv/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Piesaiste vai tīklājs ir aktīvs."</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Pieskarieties, lai to iestatītu."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Piesaiste ir atspējota"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Tīklāja un piesaistes statuss"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml deleted file mode 100644 index 052ca091ac14..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Warmkol het nie internet nie"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Toestelle kan nie aan internet koppel nie"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Skakel warmkol af"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Warmkol is aan"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Bykomende heffings kan geld terwyl jy swerf"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Gaan voort"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml deleted file mode 100644 index 0518c5a14f22..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"መገናኛ ነጥቡ በይነመረብ የለውም"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"መሣሪያዎች ከበይነመረብ ጋር መገናኘት አይችሉም"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"መገናኛ ነጥብ ያጥፉ"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"የመገናኛ ነጥብ በርቷል"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ቀጥል"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml deleted file mode 100644 index e6d8423f4633..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"نقطة الاتصال غير متصلة بالإنترنت."</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"لا يمكن للأجهزة الاتصال بالإنترنت."</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"إيقاف نقطة الاتصال"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"نقطة الاتصال مفعّلة"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"متابعة"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml deleted file mode 100644 index 4c57f21eaeb3..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"হটস্পটৰ কোনো ইণ্টাৰনেট নাই"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ডিভাইচসমূহ ইণ্টাৰনেটৰ সৈতে সংযোগ কৰিব নোৱাৰি"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"হটস্পট অফ কৰক"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"হটস্পট অন হৈ আছে"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"অব্যাহত ৰাখক"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml deleted file mode 100644 index 2610ab1bec8d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotun internetə girişi yoxdur"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Cihazlar internetə qoşula bilmir"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot\'u deaktiv edin"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktivdir"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Davam edin"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml deleted file mode 100644 index 7b032badf09a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nema pristup internetu"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji ne mogu da se povežu na internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je uključen"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Možda važe dodatni troškovi u romingu"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml deleted file mode 100644 index 2362a1e6a539..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хот-спот не падключаны да інтэрнэту"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Прылады не могуць падключацца да інтэрнэту"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Выключыць хот-спот"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хот-спот уключаны"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Працягнуць"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml deleted file mode 100644 index 6ef1b0bbaf62..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точката за достъп няма връзка с интернет"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Устройствата не могат да се свържат с интернет"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Изключване на точката за достъп"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точката за достъп е включена"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Напред"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml deleted file mode 100644 index 9a3033c94d2a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"হটস্পটের সাথে ইন্টারনেট কানেক্ট করা নেই"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ডিভাইস ইন্টারনেটের সাথে কানেক্ট করতে পারছে না"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"হটস্পট বন্ধ করুন"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"হটস্পট চালু আছে"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"চালিয়ে যান"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml deleted file mode 100644 index 57f6d88a4eba..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Pristupna tačka nema internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji se ne mogu povezati na internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi pristupnu tačku"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Pristupna tačka je uključena"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Mogu nastati dodatni troškovi u romingu"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml deleted file mode 100644 index e3ad666c0bd4..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"El punt d\'accés Wi‑Fi no té accés a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Els dispositius no es poden connectar a Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactiva el punt d\'accés Wi‑Fi"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"El punt d\'accés Wi‑Fi està activat"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"És possible que s\'apliquin costos addicionals en itinerància"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continua"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml deleted file mode 100644 index f0992814c128..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nemá připojení k internetu"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Zařízení se nemohou připojit k internetu"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vypnout hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je aktivní"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Při roamingu mohou být účtovány dodatečné poplatky"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Pokračovat"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml deleted file mode 100644 index 1fb2374487e8..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspottet har intet internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheder kan ikke oprette forbindelse til internettet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Deaktiver hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspottet er aktiveret"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Der opkræves muligvis yderligere gebyrer ved roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsæt"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml deleted file mode 100644 index 56d1d1df58a8..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot ist nicht mit dem Internet verbunden"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Geräte können nicht mit dem Internet verbunden werden"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot deaktivieren"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktiviert"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Für das Roaming können zusätzliche Gebühren anfallen"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Weiter"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml deleted file mode 100644 index 674f1f6798ed..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Το σημείο πρόσβασης Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο."</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Δεν είναι η δυνατή η σύνδεση των συσκευών στο διαδίκτυο."</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Απενεργοποίηση σημείου πρόσβασης Wi-Fi"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Σημείο πρόσβασης Wi-Fi ενεργό"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Συνέχεια"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml deleted file mode 100644 index 3046a3725d13..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml deleted file mode 100644 index 20c9b94cd5db..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml deleted file mode 100644 index 956547cc6d0c..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"El hotspot no tiene conexión a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Los dispositivos no pueden conectarse a Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactiva el hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"El hotspot está activado"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Es posible que se apliquen cargos adicionales por roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml deleted file mode 100644 index 831ec1fb1e2c..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"El punto de acceso no tiene conexión a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Los dispositivos no se pueden conectar a Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactivar punto de acceso"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Punto de acceso activado"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Puede que se apliquen cargos adicionales en itinerancia"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml deleted file mode 100644 index ff8dde542261..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Kuumkohal puudub Interneti-ühendus"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Seadmed ei saa Internetiga ühendust luua"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Lülita kuumkoht välja"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Kuumkoht on sees"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rändluse kasutamisega võivad kaasneda lisatasud"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Jätka"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml deleted file mode 100644 index c4f70a3eb48b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Sare publikoak ez du Interneteko konexiorik"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Gailuak ezin dira konektatu Internetera"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desaktibatu sare publikoa"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Sare publikoa aktibatuta dago"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Egin aurrera"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml deleted file mode 100644 index 79e3ef11d6e5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"نقطه اتصال به اینترنت دسترسی ندارد"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"دستگاهها به اینترنت متصل نشدند"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"نقطه اتصال را خاموش کنید"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"نقطه اتصال روشن است"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ادامه"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml deleted file mode 100644 index 64921bca9fe5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotilla ei ole internetyhteyttä"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Laitteet eivät voi yhdistää internetiin"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Laita hotspot pois päältä"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot on päällä"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Roaming voi aiheuttaa lisämaksuja"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Jatka"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml deleted file mode 100644 index eda7b59761cd..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Le point d\'accès n\'est pas connecté à Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Appareils non connectés à Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Désactiver le point d\'accès"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Le point d\'accès est activé"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuer"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml deleted file mode 100644 index eda7b59761cd..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Le point d\'accès n\'est pas connecté à Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Appareils non connectés à Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Désactiver le point d\'accès"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Le point d\'accès est activé"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuer"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml deleted file mode 100644 index c163c61fbd8c..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"A zona wifi non ten acceso a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Os dispositivos non se poden conectar a Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactivar zona wifi"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A zona wifi está activada"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pódense aplicar cargos adicionais en itinerancia"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml deleted file mode 100644 index 796d42ec5271..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"હૉટસ્પૉટથી ઇન્ટરનેટ ચાલી રહ્યું નથી"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ડિવાઇસ, ઇન્ટરનેટ સાથે કનેક્ટ થઈ શકતા નથી"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"હૉટસ્પૉટ બંધ કરો"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"હૉટસ્પૉટ ચાલુ છે"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"આગળ વધો"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml deleted file mode 100644 index a2442009b5ab..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"हॉटस्पॉट से इंटरनेट नहीं चल रहा"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"डिवाइस इंटरनेट से कनेक्ट नहीं हो पा रहे"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हॉटस्पॉट बंद करें"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हॉटस्पॉट चालू है"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"जारी रखें"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml deleted file mode 100644 index 41618afb2e89..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Žarišna točka nema pristup internetu"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji se ne mogu povezati s internetom"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi žarišnu točku"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Žarišna je točka uključena"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"U roamingu su mogući dodatni troškovi"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml deleted file mode 100644 index 39b7a6975b39..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"A hotspot nem csatlakozik az internethez"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Az eszközök nem tudnak csatlakozni az internethez"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot kikapcsolása"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A hotspot be van kapcsolva"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Roaming során további díjak léphetnek fel"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tovább"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml deleted file mode 100644 index c14ae10ad162..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Թեժ կետը միացված չէ ինտերնետին"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Սարքերը չեն կարողանում միանալ ինտերնետին"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Անջատել թեժ կետը"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Թեժ կետը միացված է"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Շարունակել"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml deleted file mode 100644 index 1243d22d19c4..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot tidak memiliki koneksi internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Perangkat tidak dapat tersambung ke internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Nonaktifkan hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktif"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Biaya tambahan mungkin berlaku saat roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Lanjutkan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml deleted file mode 100644 index 82a7d0123455..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Heitur reitur er ekki nettengdur"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Tæki geta ekki tengst við internetið"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Slökkva á heitum reit"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Kveikt er á heitum reit"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Viðbótargjöld kunna að eiga við í reiki"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Halda áfram"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml deleted file mode 100644 index a0f52dc89bc7..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"L\'hotspot non ha accesso a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"I dispositivi non possono connettersi a Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Disattiva l\'hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot attivo"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continua"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml deleted file mode 100644 index 80807bc23258..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"לנקודה לשיתוף אינטרנט אין חיבור לאינטרנט"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"המכשירים לא יכולים להתחבר לאינטרנט"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"כיבוי הנקודה לשיתוף אינטרנט"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"הנקודה לשיתוף אינטרנט פועלת"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ייתכנו חיובים נוספים בעת נדידה"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"המשך"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml deleted file mode 100644 index 0e21a7f32292..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"アクセス ポイントがインターネットに接続されていません"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"デバイスをインターネットに接続できません"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"アクセス ポイントを OFF にする"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"アクセス ポイント: ON"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ローミング時に追加料金が発生することがあります"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"続行"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml deleted file mode 100644 index 6d3b548744ba..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"უსადენო ქსელს არ აქვს ინტერნეტზე წვდომა"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"მოწყობილობები ვერ უკავშირდება ინტერნეტს"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"გამორთეთ უსადენო ქსელი"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"უსადენო ქსელი ჩართულია"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"გაგრძელება"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml deleted file mode 100644 index 985fc3ff9914..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспотта интернет жоқ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Құрылғылар интернетке қосылмайды"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Хотспотты өшіру"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хотспот қосулы"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Жалғастыру"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml deleted file mode 100644 index 03b5cb6e4b7d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ហតស្ប៉តមិនមានអ៊ីនធឺណិតទេ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ឧបករណ៍មិនអាចភ្ជាប់អ៊ីនធឺណិតបានទេ"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"បិទហតស្ប៉ត"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ហតស្ប៉តត្រូវបានបើក"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"អាចមានការគិតថ្លៃបន្ថែម នៅពេលរ៉ូមីង"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"បន្ត"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml deleted file mode 100644 index f0adad8e214f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ಹಾಟ್ಸ್ಪಾಟ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ಇಂಟರ್ನೆಟ್ಗೆ ಸಂಪರ್ಕಗೊಳ್ಳಲು ಸಾಧನಗಳಿಗೆ ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ಹಾಟ್ಸ್ಪಾಟ್ ಆಫ್ ಮಾಡಿ"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ಹಾಟ್ಸ್ಪಾಟ್ ಆನ್ ಆಗಿದೆ"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ರೋಮಿಂಗ್ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ಮುಂದುವರಿಸಿ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml deleted file mode 100644 index 9218e9a09b58..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"핫스팟이 인터넷에 연결되지 않음"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"기기를 인터넷에 연결할 수 없음"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"핫스팟 사용 중지"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"핫스팟 사용 중"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"계속"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml deleted file mode 100644 index 35a060aa243a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Байланыш түйүнүндө Интернет жок"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Түзмөктөр Интернетке туташпай жатат"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Туташуу түйүнүн өчүрүү"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Кошулуу түйүнү күйүк"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роумингде кошумча акы алынышы мүмкүн"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Улантуу"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml deleted file mode 100644 index 1d9203b3690b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ຮັອດສະປອດບໍ່ມີອິນເຕີເນັດ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ອິນເຕີເນັດໄດ້"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ປິດຮັອດສະປອດ"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ຮັອດສະປອດເປີດຢູ່"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ສືບຕໍ່"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml deleted file mode 100644 index db5178bf2d57..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Nėra viešosios interneto prieigos taško interneto ryšio"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Įrenginiams nepavyksta prisijungti prie interneto"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Išjungti viešosios interneto prieigos tašką"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Viešosios interneto prieigos taškas įjungtas"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tęsti"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml deleted file mode 100644 index c712173ca2b6..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Tīklājam nav interneta savienojuma"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Ierīces nevar izveidot savienojumu ar internetu"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Izslēgt tīklāju"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Tīklājs ir ieslēgts"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tālāk"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml deleted file mode 100644 index aa4490912b87..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точката на пристап нема интернет"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Уредите не може да се поврзат на интернет"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Исклучи ја точката на пристап"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точката на пристап е вклучена"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"При роаминг може да се наплатат дополнителни трошоци"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продолжи"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml deleted file mode 100644 index d376fe58704a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ഹോട്ട്സ്പോട്ടിൽ ഇന്റർനെറ്റ് ലഭ്യമല്ല"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ഉപകരണങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്യാനാവില്ല"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ഹോട്ട്സ്പോട്ട് ഓഫാക്കുക"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ഹോട്ട്സ്പോട്ട് ഓണാണ്"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"തുടരുക"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml deleted file mode 100644 index 417213f543e9..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Сүлжээний цэг дээр интернэт алга байна"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Төхөөрөмжүүд нь интернэтэд холбогдох боломжгүй байна"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Сүлжээний цэгийг унтраах"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Сүлжээний цэг асаалттай байна"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Үргэлжлүүлэх"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml deleted file mode 100644 index 2ed153fb17a2..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"हॉटस्पॉटला इंटरनेट नाही"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"डिव्हाइस इंटरनेटला कनेक्ट करू शकत नाहीत"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हॉटस्पॉट बंद करा"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हॉटस्पॉट सुरू आहे"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"सुरू ठेवा"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml deleted file mode 100644 index 50817fd4a24b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Tempat liputan tiada Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Peranti tidak dapat menyambung kepada Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Matikan tempat liputan"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Tempat liputan dihidupkan"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Caj tambahan mungkin digunakan semasa perayauan"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Teruskan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml deleted file mode 100644 index c0d70e3d5f11..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ဟော့စပေါ့တွင် အင်တာနက်မရှိပါ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"စက်များက အင်တာနက်ချိတ်ဆက်၍ မရပါ"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ဟော့စပေါ့ ပိတ်ရန်"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ဟော့စပေါ့ ဖွင့်ထားသည်"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ရှေ့ဆက်ရန်"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml deleted file mode 100644 index 1e7f1c6d0a9d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Wi-Fi-sonen har ikke internettilgang"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheter kan ikke koble til internett"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Slå av Wi-Fi-sonen"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Wi-Fi-sonen er på"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ytterligere kostnader kan påløpe under roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsett"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml deleted file mode 100644 index 63ce1550345e..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"हटस्पटमा इन्टरनेट छैन"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"यन्त्रहरू इन्टरनेटमा कनेक्ट गर्न सकिएन"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हटस्पट निष्क्रिय पार्नुहोस्"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हटस्पट सक्रिय छ"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"जारी राख्नुहोस्"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml deleted file mode 100644 index bf14a0fceda6..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot heeft geen internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Apparaten kunnen geen verbinding maken met internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot uitschakelen"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is ingeschakeld"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Doorgaan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml deleted file mode 100644 index ab87b76cafd5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ହଟସ୍ପଟରେ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ଡିଭାଇସଗୁଡ଼ିକ ଇଣ୍ଟର୍ନେଟ୍ ସହ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ହଟସ୍ପଟ ବନ୍ଦ କରନ୍ତୁ"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ହଟସ୍ପଟ ଚାଲୁ ଅଛି"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ଜାରି ରଖନ୍ତୁ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml deleted file mode 100644 index b09f285c2d0e..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ਹੌਟਸਪੌਟ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"ਡੀਵਾਈਸ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਹੋ ਸਕਦੇ"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ਹੌਟਸਪੌਟ ਬੰਦ ਕਰੋ"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ਹੌਟਸਪੌਟ ਚਾਲੂ ਹੈ"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ਜਾਰੀ ਰੱਖੋ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml deleted file mode 100644 index 8becd0715f6f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nie ma internetu"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Urządzenia nie mogą połączyć się z internetem"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Wyłącz hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot jest włączony"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Dalej"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml deleted file mode 100644 index 8e01736f643f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"O ponto de acesso não tem conexão com a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não foi possível conectar os dispositivos à Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar ponto de acesso"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"O ponto de acesso está ativado"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pode haver cobranças extras durante o roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml deleted file mode 100644 index 2356379e2f8f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"A zona Wi-Fi não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não é possível ligar os dispositivos à Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar zona Wi-Fi"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A zona Wi-Fi está ativada"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Podem aplicar-se custos adicionais em roaming."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml deleted file mode 100644 index 8e01736f643f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"O ponto de acesso não tem conexão com a Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não foi possível conectar os dispositivos à Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar ponto de acesso"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"O ponto de acesso está ativado"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pode haver cobranças extras durante o roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml deleted file mode 100644 index 2e62bd611cff..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotul nu are internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Dispozitivele nu se pot conecta la internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Dezactivați hotspotul"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspotul este activ"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Se pot aplica taxe suplimentare pentru roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuați"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml deleted file mode 100644 index a2b1640cb22f..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точка доступа не подключена к Интернету"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Устройства не могут подключаться к Интернету"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Отключить точку доступа"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точка доступа включена"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продолжить"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml deleted file mode 100644 index 632748a3e8f5..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"හොට්ස්පොට් හට අන්තර්ජාලය නැත"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"උපාංගවලට අන්තර්ජාලයට සම්බන්ධ විය නොහැකිය"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"හොට්ස්පොට් ක්රියාවිරහිත කරන්න"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"හොට්ස්පොට් ක්රියාත්මකයි"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ඉදිරියට යන්න"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml deleted file mode 100644 index 247fc1b0e738..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nemá internetové pripojenie"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Zariadenia sa nedajú pripojiť k internetu"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vypnúť hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je zapnutý"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Pokračovať"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml deleted file mode 100644 index ed223721978a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Dostopna točka nima internetne povezave"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Naprave ne morejo vzpostaviti internetne povezave"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Izklopi dostopno točko"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Dostopna točka je vklopljena"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Med gostovanjem lahko nastanejo dodatni stroški"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Naprej"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml deleted file mode 100644 index 4bfab6e47449..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Zona e qasjes për internet nuk ka internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Pajisjet nuk mund të lidhen me internetin"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Çaktivizo zonën e qasjes për internet"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Zona e qasjes për internet është aktive"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Mund të zbatohen tarifime shtesë kur je në roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Vazhdo"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml deleted file mode 100644 index 478d53a25587..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспот нема приступ интернету"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Уређаји не могу да се повежу на интернет"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Искључи хотспот"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хотспот је укључен"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Можда важе додатни трошкови у ромингу"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Настави"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml deleted file mode 100644 index a793ed648347..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Surfzonen har ingen internetanslutning"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheterna har ingen internetanslutning"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Inaktivera surfzon"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Surfzonen är aktiverad"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ytterligare avgifter kan tillkomma vid roaming"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsätt"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml deleted file mode 100644 index 18ee457d03cc..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Mtandao pepe hauna intaneti"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Vifaa vimeshindwa kuunganisha kwenye intaneti"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Zima mtandao pepe"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Mtandao pepe umewashwa"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Endelea"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml deleted file mode 100644 index 7eebd6784ac1..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ஹாட்ஸ்பாட்டில் இணையம் இல்லை"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"சாதனங்களால் இணையத்தில் இணைய இயலவில்லை"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ஹாட்ஸ்பாட்டை ஆஃப் செய்"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ஹாட்ஸ்பாட் ஆன் செய்யப்பட்டுள்ளது"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"தொடர்க"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml deleted file mode 100644 index 0986534fc7bc..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"హాట్స్పాట్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"పరికరాలను ఇంటర్నెట్కి కనెక్ట్ చేయడం సాధ్యం కాదు"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"హాట్స్పాట్ని ఆఫ్ చేయండి"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"హాట్స్పాట్ ఆన్లో ఉంది"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"రోమింగ్లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"కొనసాగించు"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml deleted file mode 100644 index 3837002b29a8..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ฮอตสปอตไม่ได้เชื่อมต่ออินเทอร์เน็ต"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"อุปกรณ์เชื่อมต่ออินเทอร์เน็ตไม่ได้"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ปิดฮอตสปอต"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ฮอตสปอตเปิดอยู่"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ต่อไป"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml deleted file mode 100644 index 208f893447de..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Walang internet ang hotspot"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Hindi makakonekta sa internet ang mga device"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"I-off ang hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Naka-on ang hotspot"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Ituloy"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml deleted file mode 100644 index 3482fafa2d9d..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot\'un internet bağlantısı yok"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Cihazlar internete bağlanamıyor"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot\'u kapat"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot açık"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Dolaşım sırasında ek ücretler uygulanabilir"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Devam"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml deleted file mode 100644 index dea311443fda..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точка доступу не підключена до Інтернету"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Не вдається підключити пристрої до Інтернету"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Вимкнути точку доступу"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точку доступу ввімкнено"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"У роумінгу може стягуватися додаткова плата"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продовжити"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml deleted file mode 100644 index 09bc0c9eabb9..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"ہاٹ اسپاٹ میں انٹرنیٹ نہیں ہے"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"آلات انٹرنیٹ سے منسلک نہیں ہو سکتے"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ہاٹ اسپاٹ آف کریں"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ہاٹ اسپاٹ آن ہے"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"جاری رکھیں"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml deleted file mode 100644 index 715d34808b48..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot internetga ulanmagan"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Qurilmalar internetga ulana olmayapti"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspotni faolsizlantirish"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot yoniq"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Davom etish"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml deleted file mode 100644 index bf4ee1011b41..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"Điểm phát sóng không có kết nối Internet"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Các thiết bị không thể kết nối Internet"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Tắt điểm phát sóng"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Điểm phát sóng đang bật"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tiếp tục"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml deleted file mode 100644 index cdb4224bf37b..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"热点没有网络连接"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"设备无法连接到互联网"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"关闭热点"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"热点已开启"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"漫游时可能会产生额外的费用"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"继续"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml deleted file mode 100644 index 3bb52e491f0a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"熱點沒有互聯網連線"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"裝置無法連線至互聯網"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"關閉熱點"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"已開啟熱點"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"漫遊時可能需要支付額外費用"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"繼續"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml deleted file mode 100644 index 298c3eac701a..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"無線基地台沒有網際網路連線"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"裝置無法連上網際網路"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"關閉無線基地台"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"無線基地台已開啟"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"使用漫遊服務可能須支付額外費用"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"繼續"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml deleted file mode 100644 index 3dc0078834c9..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="6246167638178412020">"I-Hotspot ayina-inthanethi"</string> - <string name="no_upstream_notification_message" msgid="5010177541603431003">"Amadivayisi awakwazi ukuxhuma ku-inthanethi"</string> - <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vala i-hotspot"</string> - <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"I-Hotspot ivuliwe"</string> - <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string> - <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Qhubeka"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml deleted file mode 100644 index 19d659c6ce36..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Verbinding het nie internet nie"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Toestelle kan nie koppel nie"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Skakel verbinding af"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Warmkol of verbinding is aan"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bykomende heffings kan geld terwyl jy swerf"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml deleted file mode 100644 index 8995430b4f09..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ማስተሳሰር ምንም በይነመረብ የለውም"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"መሣሪያዎችን ማገናኘት አይቻልም"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ማስተሳሰርን አጥፋ"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml deleted file mode 100644 index 54f3b5389ae9..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ما مِن اتصال بالإنترنت خلال التوصيل"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"تعذّر اتصال الأجهزة"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"إيقاف التوصيل"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"نقطة الاتصال أو التوصيل مفعّلان"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml deleted file mode 100644 index e215141c9eb6..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টে\'ডাৰিং অফ কৰক"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml deleted file mode 100644 index 1fd8e4c963a7..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemin internetə girişi yoxdur"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazları qoşmaq mümkün deyil"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modemi deaktiv edin"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot və ya modem aktivdir"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml deleted file mode 100644 index 1abe4f3aa3c7..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Privezivanje nema pristup internetu"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Povezivanje uređaja nije uspelo"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi privezivanje"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključen je hotspot ili privezivanje"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Možda važe dodatni troškovi u romingu"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml deleted file mode 100644 index 38dbd1e3914f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не ўдалося падключыць прылады"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Выключыць рэжым мадэма"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хот-спот або рэжым мадэма ўключаны"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml deleted file mode 100644 index 04b44db5c1a4..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетърингът няма връзка с интернет"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Устройствата не могат да установят връзка"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Изключване на тетъринга"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката за достъп или тетърингът са включени"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml deleted file mode 100644 index 579d1be1c1ea..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইস কানেক্ট করতে পারছে না"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টিথারিং বন্ধ করুন"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট বা টিথারিং চালু আছে"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml deleted file mode 100644 index 9ce3efe6c39d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Povezivanje putem mobitela nema internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi povezivanje putem mobitela"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mogu nastati dodatni troškovi u romingu"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml deleted file mode 100644 index 46d4c35b9b83..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"La compartició de xarxa no té accés a Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"No es poden connectar els dispositius"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactiva la compartició de xarxa"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"És possible que s\'apliquin costos addicionals en itinerància"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml deleted file mode 100644 index cc13860b3da1..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá připojení k internetu"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zařízení se nemůžou připojit"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnout tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot nebo tethering"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Při roamingu mohou být účtovány dodatečné poplatky"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml deleted file mode 100644 index 92c3ae11567d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Netdeling har ingen internetforbindelse"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheder kan ikke oprette forbindelse"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Deaktiver netdeling"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot eller netdeling er aktiveret"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Der opkræves muligvis yderligere gebyrer ved roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml deleted file mode 100644 index 967eb4db2e77..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering hat keinen Internetzugriff"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Geräte können sich nicht verbinden"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering deaktivieren"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot oder Tethering ist aktiviert"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Für das Roaming können zusätzliche Gebühren anfallen"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml deleted file mode 100644 index 5fb497451f6d..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Απενεργοποιήστε τη σύνδεση"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml deleted file mode 100644 index 45647f93f246..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml deleted file mode 100644 index 45647f93f246..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml deleted file mode 100644 index 45647f93f246..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml deleted file mode 100644 index 45647f93f246..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml deleted file mode 100644 index 7877074afc66..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml deleted file mode 100644 index 08edd81a6b04..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión mediante dispositivo móvil no tiene Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"No se pueden conectar los dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión mediante dispositivo móvil"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Es posible que se apliquen cargos adicionales por roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml deleted file mode 100644 index 79f51d00e2e8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión no se puede compartir, porque no hay acceso a Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Los dispositivos no se pueden conectar"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Punto de acceso o conexión compartida activados"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Puede que se apliquen cargos adicionales en itinerancia"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml deleted file mode 100644 index 2da5f8a6d62a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Jagamisel puudub internetiühendus"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Seadmed ei saa ühendust luua"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Lülita jagamine välja"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kuumkoht või jagamine on sisse lülitatud"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rändluse kasutamisega võivad kaasneda lisatasud"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml deleted file mode 100644 index 2073f2806c18..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Ezin dira konektatu gailuak"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desaktibatu konexioa partekatzeko aukera"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml deleted file mode 100644 index e21b2a0852c7..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"«اشتراکگذاری اینترنت» به اینترنت دسترسی ندارد"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"دستگاهها متصل نمیشوند"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"خاموش کردن «اشتراکگذاری اینترنت»"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"«نقطه اتصال» یا «اشتراکگذاری اینترنت» روشن است"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml deleted file mode 100644 index 88b0b13eb4b5..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ei jaettavaa internetyhteyttä"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Laitteet eivät voi muodostaa yhteyttä"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Laita yhteyden jakaminen pois päältä"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot tai yhteyden jakaminen on päällä"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming voi aiheuttaa lisämaksuja"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml deleted file mode 100644 index 3b781bc8db31..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Le partage de connexion n\'est pas connecté à Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml deleted file mode 100644 index 51d7203c3652..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml deleted file mode 100644 index 008ccb475d66..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"A conexión compartida non ten Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Non se puideron conectar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Está activada a zona wifi ou a conexión compartida"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pódense aplicar cargos adicionais en itinerancia"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml deleted file mode 100644 index f2e3b4df782f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml deleted file mode 100644 index b11839d760c8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंग से इंटरनेट नहीं चल रहा"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिवाइस कनेक्ट नहीं हो पा रहे"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करें"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट या टेदरिंग चालू है"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml deleted file mode 100644 index 0a5aca25b1a9..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemsko povezivanje nema internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključivanje modemskog povezivanja"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključena je žarišna točka ili modemsko povezivanje"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"U roamingu su mogući dodatni troškovi"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml deleted file mode 100644 index 21c689a44ef8..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nincs internetkapcsolat az internet megosztásához"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Az eszközök nem tudnak csatlakozni"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Internetmegosztás kikapcsolása"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A hotspot vagy az internetmegosztás be van kapcsolva"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming során további díjak léphetnek fel"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml deleted file mode 100644 index 689d92870e50..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Մոդեմի ռեժիմի կապը բացակայում է"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Չհաջողվեց միացնել սարքը"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Անջատել մոդեմի ռեժիմը"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml deleted file mode 100644 index a5f4d19abfe9..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tidak ada koneksi internet di tethering"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Perangkat tidak dapat terhubung"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Nonaktifkan tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot atau tethering aktif"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Biaya tambahan mungkin berlaku saat roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml deleted file mode 100644 index fc7e8aaf4e42..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tjóðrun er ekki með internettengingu"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Tæki geta ekki tengst"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slökkva á tjóðrun"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kveikt er á heitum reit eða tjóðrun"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viðbótargjöld kunna að eiga við í reiki"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml deleted file mode 100644 index 6456dd1b806a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nessuna connessione a Internet per il tethering"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossibile connettere i dispositivi"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Disattiva il tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot o tethering attivi"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml deleted file mode 100644 index 46b24bd3c508..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"למכשירים אין אפשרות להתחבר"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"השבתה של שיתוף האינטרנט בין מכשירים"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ייתכנו חיובים נוספים בעת נדידה"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml deleted file mode 100644 index e6eb277b90dd..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"テザリングがインターネットに接続されていません"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"デバイスを接続できません"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"テザリングを OFF にする"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"アクセス ポイントまたはテザリングが ON です"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ローミング時に追加料金が発生することがあります"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml deleted file mode 100644 index aeddd7101da0..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ტეტერინგის გამორთვა"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml deleted file mode 100644 index 255f0a276f95..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Құрылғыларды байланыстыру мүмкін емес"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Тетерингіні өшіру"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хотспот немесе тетеринг қосулы"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml deleted file mode 100644 index 2bceb1cf7788..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ការភ្ជាប់មិនមានអ៊ីនធឺណិតទេ"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"មិនអាចភ្ជាប់ឧបករណ៍បានទេ"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"បិទការភ្ជាប់"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ហតស្ប៉ត ឬការភ្ជាប់ត្រូវបានបើក"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"អាចមានការគិតថ្លៃបន្ថែម នៅពេលរ៉ូមីង"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml deleted file mode 100644 index ed769305a679..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ಟೆಥರಿಂಗ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ಟೆಥರಿಂಗ್ ಆಫ್ ಮಾಡಿ"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ಹಾಟ್ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್ ಆನ್ ಆಗಿದೆ"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ರೋಮಿಂಗ್ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml deleted file mode 100644 index 6e504941eb8b..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"테더링으로 인터넷을 사용할 수 없음"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"기기에서 연결할 수 없음"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"테더링 사용 중지"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"핫스팟 또는 테더링 켜짐"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml deleted file mode 100644 index d68128b9a554..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модем режими Интернети жок колдонулууда"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Түзмөктөр туташпай жатат"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем режимин өчүрүү"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Байланыш түйүнү же модем режими күйүк"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингде кошумча акы алынышы мүмкүн"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml deleted file mode 100644 index 03e134a0fc79..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ປິດການປ່ອຍສັນຍານ"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml deleted file mode 100644 index 652cedc6e6ae..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nepavyko susieti įrenginių"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Išjungti įrenginio kaip modemo naudojimą"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml deleted file mode 100644 index 221972298c18..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Piesaistei nav interneta savienojuma"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nevar savienot ierīces"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izslēgt piesaisti"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ir ieslēgts tīklājs vai piesaiste"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml deleted file mode 100644 index 227f9e346651..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Нема интернет преку мобилен"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Уредите не може да се поврзат"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Исклучи интернет преку мобилен"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката на пристап или интернетот преку мобилен е вклучен"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"При роаминг може да се наплатат дополнителни трошоци"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml deleted file mode 100644 index ec4388512645..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ടെതറിംഗ് ഓഫാക്കുക"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ഹോട്ട്സ്പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml deleted file mode 100644 index e263573799ed..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модемд интернэт алга байна"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем болгохыг унтраах"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml deleted file mode 100644 index adf845d078bf..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंगला इंटरनेट नाही"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करा"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml deleted file mode 100644 index f65c451e4c21..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Penambatan tiada Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Peranti tidak dapat disambungkan"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Matikan penambatan"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Tempat liputan atau penambatan dihidupkan"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Caj tambahan mungkin digunakan semasa perayauan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml deleted file mode 100644 index 4118e775cd84..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"စက်များ ချိတ်ဆက်၍ မရပါ"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml deleted file mode 100644 index 36853583ce82..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internettdeling har ikke internettilgang"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enhetene kan ikke koble til"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slå av internettdeling"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wi-Fi-sone eller internettdeling er på"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligere kostnader kan påløpe under roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml deleted file mode 100644 index d074f1569933..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिङ निष्क्रिय पार्नुहोस्"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हटस्पट वा टेदरिङ सक्रिय छ"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml deleted file mode 100644 index 1d888942f49b..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering heeft geen internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Apparaten kunnen niet worden verbonden"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering uitschakelen"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot of tethering is ingeschakeld"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml deleted file mode 100644 index 8038815fe804..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml deleted file mode 100644 index 819833eab07f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml deleted file mode 100644 index 65e4380e3916..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nie ma internetu"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Urządzenia nie mogą się połączyć"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Wyłącz tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot lub tethering jest włączony"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml deleted file mode 100644 index d8866170c191..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml deleted file mode 100644 index bfd45ca0a3e1..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"A ligação (à Internet) via telemóvel não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível ligar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar ligação (à Internet) via telemóvel"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podem aplicar-se custos adicionais em roaming."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml deleted file mode 100644 index d8866170c191..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml deleted file mode 100644 index 8d87a9e516ad..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Procesul de tethering nu are internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Dispozitivele nu se pot conecta"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Dezactivați procesul de tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S-a activat hotspotul sau tethering"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Se pot aplica taxe suplimentare pentru roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml deleted file mode 100644 index dbdb9ebe4931..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Режим модема используется без доступа к Интернету"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Невозможно подключить устройства."</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Отключить режим модема"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Включены точка доступа или режим модема"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml deleted file mode 100644 index d8301e41c2b2..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ටෙදරින් හට අන්තර්ජාලය නැත"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ටෙදරින් ක්රියාවිරහිත කරන්න"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"හොට්ස්පොට් හෝ ටෙදරින් ක්රියාත්මකයි"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml deleted file mode 100644 index bef71363f450..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá internetové pripojenie"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zariadenia sa nemôžu pripojiť"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnúť tethering"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot alebo tethering"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml deleted file mode 100644 index 3202c62e8a3a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Napravi se ne moreta povezati"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izklopi internetno povezavo prek mobilnega telefona"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Med gostovanjem lahko nastanejo dodatni stroški"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml deleted file mode 100644 index 37f6ad286880..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ndarja e internetit nuk ka internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Pajisjet nuk mund të lidhen"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Çaktivizo ndarjen e internetit"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mund të zbatohen tarifime shtesë kur je në roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml deleted file mode 100644 index 5566d03ed13a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Привезивање нема приступ интернету"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Повезивање уређаја није успело"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Искључи привезивање"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Укључен је хотспот или привезивање"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Можда важе додатни трошкови у ромингу"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml deleted file mode 100644 index 9765acd0cf46..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Det finns ingen internetanslutning för internetdelningen"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheterna kan inte anslutas"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Inaktivera internetdelning"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Surfzon eller internetdelning har aktiverats"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligare avgifter kan tillkomma vid roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml deleted file mode 100644 index cf850c9cd222..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Kipengele cha kusambaza mtandao hakina intaneti"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Imeshindwa kuunganisha vifaa"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Zima kipengele cha kusambaza mtandao"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml deleted file mode 100644 index f4b15aab19b7..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"சாதனங்களால் இணைய முடியவில்லை"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"இணைப்பு முறையை ஆஃப் செய்"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml deleted file mode 100644 index 937d34d52027..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"టెథరింగ్ను ఆఫ్ చేయండి"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"హాట్స్పాట్ లేదా టెథరింగ్ ఆన్లో ఉంది"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"రోమింగ్లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml deleted file mode 100644 index f781fae5252e..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"อุปกรณ์เชื่อมต่อไม่ได้"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml deleted file mode 100644 index 8d5d46537334..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Walang internet ang pag-tether"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Hindi makakonekta ang mga device"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"I-off ang pag-tether"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Naka-on ang Hotspot o pag-tether"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml deleted file mode 100644 index 80cab33ac05e..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering\'in internet bağlantısı yok"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazlar bağlanamıyor"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering\'i kapat"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot veya tethering açık"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Dolaşım sırasında ek ücretler uygulanabilir"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml deleted file mode 100644 index c05932a5ae7f..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Телефон, який використовується як модем, не підключений до Інтернету"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не вдається підключити пристрої"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Вимкнути використання телефона як модема"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Увімкнено точку доступу або використання телефона як модема"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"У роумінгу може стягуватися додаткова плата"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml deleted file mode 100644 index d820eee8ba3c..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"آلات منسلک نہیں ہو سکتے"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ٹیدرنگ آف کریں"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml deleted file mode 100644 index 726148aaee5a..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modem internetga ulanmagan"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Qurilmalar ulanmadi"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modem rejimini faolsizlantirish"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot yoki modem rejimi yoniq"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml deleted file mode 100644 index b7cb0456b673..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Không có Internet để chia sẻ kết Internet"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Các thiết bị không thể kết nối"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tắt tính năng chia sẻ Internet"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml deleted file mode 100644 index af91afff9a4c..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"共享网络未连接到互联网"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"设备无法连接"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"关闭网络共享"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"热点或网络共享已开启"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫游时可能会产生额外的费用"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml deleted file mode 100644 index 28e6b80c01a9..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網絡共享連線至互聯網"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連接"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網絡共享"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"熱點或網絡共享已開啟"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫遊時可能需要支付額外費用"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml deleted file mode 100644 index 528a1e52925c..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網路共用連上網際網路"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連線"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網路共用"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"無線基地台或網路共用已開啟"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"使用漫遊服務可能須支付額外費用"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml deleted file mode 100644 index 11eb66621971..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string> - <string name="no_upstream_notification_message" msgid="3843613362272973447">"Amadivayisi awakwazi ukuxhumeka"</string> - <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vala ukusebenzisa ifoni njengemodemu"</string> - <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string> - <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml deleted file mode 100644 index 5c5be0466a36..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004/config.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to - "0" for disable this feature. --> - <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer> - - <!-- Config for showing upstream roaming notification. --> - <bool name="config_upstream_roaming_notification">true</bool> -</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mcc310-mnc004/strings.xml b/packages/Tethering/res/values-mcc310-mnc004/strings.xml deleted file mode 100644 index ce9ff6080717..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc004/strings.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_title">Tethering has no internet</string> - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_message">Devices can\u2019t connect</string> - <!-- String for no upstream notification disable button [CHAR LIMIT=200] --> - <string name="no_upstream_notification_disable_button">Turn off tethering</string> - - <!-- String for cellular roaming notification title [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string> - <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> - <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml deleted file mode 100644 index 9bfa5317a9e4..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Verbinding het nie internet nie"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Toestelle kan nie koppel nie"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Skakel verbinding af"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Warmkol of verbinding is aan"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bykomende heffings kan geld terwyl jy swerf"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml deleted file mode 100644 index 5949dfa776d7..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ማስተሳሰር ምንም በይነመረብ የለውም"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"መሣሪያዎችን ማገናኘት አይቻልም"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ማስተሳሰርን አጥፋ"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml deleted file mode 100644 index 8467f9b1f5cf..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ما مِن اتصال بالإنترنت خلال التوصيل"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"تعذّر اتصال الأجهزة"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"إيقاف التوصيل"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"نقطة الاتصال أو التوصيل مفعّلان"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml deleted file mode 100644 index 9776bd89da48..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টে\'ডাৰিং অফ কৰক"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml deleted file mode 100644 index e6d3eaf9f07c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemin internetə girişi yoxdur"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazları qoşmaq mümkün deyil"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modemi deaktiv edin"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot və ya modem aktivdir"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml deleted file mode 100644 index 4c8a1df8eece..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Privezivanje nema pristup internetu"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Povezivanje uređaja nije uspelo"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi privezivanje"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključen je hotspot ili privezivanje"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Možda važe dodatni troškovi u romingu"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml deleted file mode 100644 index edfa41e1ffd3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не ўдалося падключыць прылады"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Выключыць рэжым мадэма"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хот-спот або рэжым мадэма ўключаны"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml deleted file mode 100644 index f56398196f51..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетърингът няма връзка с интернет"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Устройствата не могат да установят връзка"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Изключване на тетъринга"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката за достъп или тетърингът са включени"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml deleted file mode 100644 index d8ecd2e988f9..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইস কানেক্ট করতে পারছে না"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টিথারিং বন্ধ করুন"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট বা টিথারিং চালু আছে"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml deleted file mode 100644 index b85fd5e28577..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Povezivanje putem mobitela nema internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi povezivanje putem mobitela"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mogu nastati dodatni troškovi u romingu"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml deleted file mode 100644 index a3572151be6b..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"La compartició de xarxa no té accés a Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"No es poden connectar els dispositius"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactiva la compartició de xarxa"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"És possible que s\'apliquin costos addicionals en itinerància"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml deleted file mode 100644 index 91196be9e557..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá připojení k internetu"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zařízení se nemůžou připojit"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnout tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot nebo tethering"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Při roamingu mohou být účtovány dodatečné poplatky"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml deleted file mode 100644 index 196890011d50..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Netdeling har ingen internetforbindelse"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheder kan ikke oprette forbindelse"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Deaktiver netdeling"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot eller netdeling er aktiveret"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Der opkræves muligvis yderligere gebyrer ved roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml deleted file mode 100644 index eb3f8c52c0c5..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering hat keinen Internetzugriff"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Geräte können sich nicht verbinden"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering deaktivieren"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot oder Tethering ist aktiviert"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Für das Roaming können zusätzliche Gebühren anfallen"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml deleted file mode 100644 index 56c3d81b634e..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Απενεργοποιήστε τη σύνδεση"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml deleted file mode 100644 index dd1a1971cdd8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml deleted file mode 100644 index dd1a1971cdd8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml deleted file mode 100644 index dd1a1971cdd8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml deleted file mode 100644 index dd1a1971cdd8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml deleted file mode 100644 index d3347aae207d..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml deleted file mode 100644 index 2f0504f07de7..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión mediante dispositivo móvil no tiene Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"No se pueden conectar los dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión mediante dispositivo móvil"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Es posible que se apliquen cargos adicionales por roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml deleted file mode 100644 index 2d8f88242502..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión no se puede compartir, porque no hay acceso a Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Los dispositivos no se pueden conectar"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Punto de acceso o conexión compartida activados"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Puede que se apliquen cargos adicionales en itinerancia"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml deleted file mode 100644 index 8493c470710d..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Jagamisel puudub internetiühendus"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Seadmed ei saa ühendust luua"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Lülita jagamine välja"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kuumkoht või jagamine on sisse lülitatud"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rändluse kasutamisega võivad kaasneda lisatasud"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml deleted file mode 100644 index 33bccab3e88c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Ezin dira konektatu gailuak"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desaktibatu konexioa partekatzeko aukera"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml deleted file mode 100644 index cf8a0cc27705..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"«اشتراکگذاری اینترنت» به اینترنت دسترسی ندارد"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"دستگاهها متصل نمیشوند"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"خاموش کردن «اشتراکگذاری اینترنت»"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"«نقطه اتصال» یا «اشتراکگذاری اینترنت» روشن است"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml deleted file mode 100644 index 6a3ab806db98..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Ei jaettavaa internetyhteyttä"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Laitteet eivät voi muodostaa yhteyttä"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Laita yhteyden jakaminen pois päältä"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot tai yhteyden jakaminen on päällä"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming voi aiheuttaa lisämaksuja"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml deleted file mode 100644 index ffb9bf60472e..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Le partage de connexion n\'est pas connecté à Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml deleted file mode 100644 index 768bce3f0ab1..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml deleted file mode 100644 index 0c4195a7caf3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"A conexión compartida non ten Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Non se puideron conectar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Está activada a zona wifi ou a conexión compartida"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pódense aplicar cargos adicionais en itinerancia"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml deleted file mode 100644 index e9d33a7db259..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml deleted file mode 100644 index aa418ac5d3bb..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंग से इंटरनेट नहीं चल रहा"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिवाइस कनेक्ट नहीं हो पा रहे"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करें"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट या टेदरिंग चालू है"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml deleted file mode 100644 index 51c524afbc53..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemsko povezivanje nema internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključivanje modemskog povezivanja"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključena je žarišna točka ili modemsko povezivanje"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"U roamingu su mogući dodatni troškovi"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml deleted file mode 100644 index 164e45edd142..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Nincs internetkapcsolat az internet megosztásához"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Az eszközök nem tudnak csatlakozni"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Internetmegosztás kikapcsolása"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A hotspot vagy az internetmegosztás be van kapcsolva"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming során további díjak léphetnek fel"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml deleted file mode 100644 index e76c0a4c80d5..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Մոդեմի ռեժիմի կապը բացակայում է"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Չհաջողվեց միացնել սարքը"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Անջատել մոդեմի ռեժիմը"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml deleted file mode 100644 index 2b817f8abd17..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tidak ada koneksi internet di tethering"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Perangkat tidak dapat terhubung"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Nonaktifkan tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot atau tethering aktif"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Biaya tambahan mungkin berlaku saat roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml deleted file mode 100644 index a338d9c7cab8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tjóðrun er ekki með internettengingu"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Tæki geta ekki tengst"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slökkva á tjóðrun"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kveikt er á heitum reit eða tjóðrun"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viðbótargjöld kunna að eiga við í reiki"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml deleted file mode 100644 index 77769c2ac56c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Nessuna connessione a Internet per il tethering"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossibile connettere i dispositivi"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Disattiva il tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot o tethering attivi"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml deleted file mode 100644 index 5267b5126435..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"למכשירים אין אפשרות להתחבר"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"השבתה של שיתוף האינטרנט בין מכשירים"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ייתכנו חיובים נוספים בעת נדידה"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml deleted file mode 100644 index 66a9a6dd35c2..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"テザリングがインターネットに接続されていません"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"デバイスを接続できません"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"テザリングを OFF にする"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"アクセス ポイントまたはテザリングが ON です"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ローミング時に追加料金が発生することがあります"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml deleted file mode 100644 index d8ad8808498f..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ტეტერინგის გამორთვა"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml deleted file mode 100644 index 1ddd6b419b57..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Құрылғыларды байланыстыру мүмкін емес"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Тетерингіні өшіру"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хотспот немесе тетеринг қосулы"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml deleted file mode 100644 index cf5a1379ccc7..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ការភ្ជាប់មិនមានអ៊ីនធឺណិតទេ"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"មិនអាចភ្ជាប់ឧបករណ៍បានទេ"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"បិទការភ្ជាប់"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ហតស្ប៉ត ឬការភ្ជាប់ត្រូវបានបើក"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"អាចមានការគិតថ្លៃបន្ថែម នៅពេលរ៉ូមីង"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml deleted file mode 100644 index 68ae68bc1998..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ಟೆಥರಿಂಗ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ಟೆಥರಿಂಗ್ ಆಫ್ ಮಾಡಿ"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ಹಾಟ್ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್ ಆನ್ ಆಗಿದೆ"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ರೋಮಿಂಗ್ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml deleted file mode 100644 index 17185ba2d063..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"테더링으로 인터넷을 사용할 수 없음"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"기기에서 연결할 수 없음"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"테더링 사용 중지"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"핫스팟 또는 테더링 켜짐"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml deleted file mode 100644 index 6a9fb9810cc6..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Модем режими Интернети жок колдонулууда"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Түзмөктөр туташпай жатат"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем режимин өчүрүү"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Байланыш түйүнү же модем режими күйүк"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингде кошумча акы алынышы мүмкүн"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml deleted file mode 100644 index bcc4b5762678..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ປິດການປ່ອຍສັນຍານ"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml deleted file mode 100644 index 011c2c11fb88..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nepavyko susieti įrenginių"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Išjungti įrenginio kaip modemo naudojimą"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml deleted file mode 100644 index 5cb2f3b7aac8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Piesaistei nav interneta savienojuma"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nevar savienot ierīces"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izslēgt piesaisti"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ir ieslēgts tīklājs vai piesaiste"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml deleted file mode 100644 index 4cbfd887c57e..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Нема интернет преку мобилен"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Уредите не може да се поврзат"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Исклучи интернет преку мобилен"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката на пристап или интернетот преку мобилен е вклучен"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"При роаминг може да се наплатат дополнителни трошоци"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml deleted file mode 100644 index 9cf4eaf34a97..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ടെതറിംഗ് ഓഫാക്കുക"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ഹോട്ട്സ്പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml deleted file mode 100644 index 47c82c14d9d6..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Модемд интернэт алга байна"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем болгохыг унтраах"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml deleted file mode 100644 index ad9e809ab27d..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंगला इंटरनेट नाही"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करा"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml deleted file mode 100644 index e708cb8717b3..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Penambatan tiada Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Peranti tidak dapat disambungkan"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Matikan penambatan"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Tempat liputan atau penambatan dihidupkan"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Caj tambahan mungkin digunakan semasa perayauan"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml deleted file mode 100644 index ba5462250b05..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"စက်များ ချိတ်ဆက်၍ မရပါ"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml deleted file mode 100644 index 57db484a2543..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Internettdeling har ikke internettilgang"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enhetene kan ikke koble til"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slå av internettdeling"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wi-Fi-sone eller internettdeling er på"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligere kostnader kan påløpe under roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml deleted file mode 100644 index 1503244f5000..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिङ निष्क्रिय पार्नुहोस्"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हटस्पट वा टेदरिङ सक्रिय छ"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml deleted file mode 100644 index b08133f4e592..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering heeft geen internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Apparaten kunnen niet worden verbonden"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering uitschakelen"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot of tethering is ingeschakeld"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml deleted file mode 100644 index 1ad4ca354ad5..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml deleted file mode 100644 index 88def563d85a..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml deleted file mode 100644 index f9890abdc26b..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nie ma internetu"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Urządzenia nie mogą się połączyć"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Wyłącz tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot lub tethering jest włączony"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml deleted file mode 100644 index ce3b88479f09..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml deleted file mode 100644 index 7e883ea57682..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"A ligação (à Internet) via telemóvel não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível ligar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar ligação (à Internet) via telemóvel"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podem aplicar-se custos adicionais em roaming."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml deleted file mode 100644 index ce3b88479f09..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml deleted file mode 100644 index 1009417316ed..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Procesul de tethering nu are internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Dispozitivele nu se pot conecta"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Dezactivați procesul de tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S-a activat hotspotul sau tethering"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Se pot aplica taxe suplimentare pentru roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml deleted file mode 100644 index 88683bed95b8..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Режим модема используется без доступа к Интернету"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Невозможно подключить устройства."</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Отключить режим модема"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Включены точка доступа или режим модема"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml deleted file mode 100644 index 176bcdb797c6..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ටෙදරින් හට අන්තර්ජාලය නැත"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ටෙදරින් ක්රියාවිරහිත කරන්න"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"හොට්ස්පොට් හෝ ටෙදරින් ක්රියාත්මකයි"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml deleted file mode 100644 index b9e2127fa879..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá internetové pripojenie"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zariadenia sa nemôžu pripojiť"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnúť tethering"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot alebo tethering"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml deleted file mode 100644 index e8140e686a0c..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Napravi se ne moreta povezati"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izklopi internetno povezavo prek mobilnega telefona"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Med gostovanjem lahko nastanejo dodatni stroški"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml deleted file mode 100644 index 61e698d6e8ab..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Ndarja e internetit nuk ka internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Pajisjet nuk mund të lidhen"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Çaktivizo ndarjen e internetit"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mund të zbatohen tarifime shtesë kur je në roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml deleted file mode 100644 index b4c411c35475..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Привезивање нема приступ интернету"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Повезивање уређаја није успело"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Искључи привезивање"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Укључен је хотспот или привезивање"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Можда важе додатни трошкови у ромингу"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml deleted file mode 100644 index 4f543e47b998..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Det finns ingen internetanslutning för internetdelningen"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheterna kan inte anslutas"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Inaktivera internetdelning"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Surfzon eller internetdelning har aktiverats"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligare avgifter kan tillkomma vid roaming"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml deleted file mode 100644 index ac347ab485e0..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Kipengele cha kusambaza mtandao hakina intaneti"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Imeshindwa kuunganisha vifaa"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Zima kipengele cha kusambaza mtandao"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml deleted file mode 100644 index 2ea2467e5879..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"சாதனங்களால் இணைய முடியவில்லை"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"இணைப்பு முறையை ஆஃப் செய்"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml deleted file mode 100644 index 9360297dd807..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"టెథరింగ్ను ఆఫ్ చేయండి"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"హాట్స్పాట్ లేదా టెథరింగ్ ఆన్లో ఉంది"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"రోమింగ్లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml deleted file mode 100644 index 9c4d7e08f2b6..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"อุปกรณ์เชื่อมต่อไม่ได้"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml deleted file mode 100644 index a7c78a593267..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Walang internet ang pag-tether"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Hindi makakonekta ang mga device"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"I-off ang pag-tether"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Naka-on ang Hotspot o pag-tether"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml deleted file mode 100644 index 93da2c3f7981..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering\'in internet bağlantısı yok"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazlar bağlanamıyor"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering\'i kapat"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot veya tethering açık"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Dolaşım sırasında ek ücretler uygulanabilir"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml deleted file mode 100644 index ee0dcd2c4b6a..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Телефон, який використовується як модем, не підключений до Інтернету"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не вдається підключити пристрої"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Вимкнути використання телефона як модема"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Увімкнено точку доступу або використання телефона як модема"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"У роумінгу може стягуватися додаткова плата"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml deleted file mode 100644 index 41cd28eef9bd..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"آلات منسلک نہیں ہو سکتے"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ٹیدرنگ آف کریں"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml deleted file mode 100644 index c847bc943bd4..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Modem internetga ulanmagan"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Qurilmalar ulanmadi"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modem rejimini faolsizlantirish"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot yoki modem rejimi yoniq"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml deleted file mode 100644 index a74326f09ec5..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Không có Internet để chia sẻ kết Internet"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Các thiết bị không thể kết nối"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tắt tính năng chia sẻ Internet"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml deleted file mode 100644 index d7370036e351..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"共享网络未连接到互联网"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"设备无法连接"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"关闭网络共享"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"热点或网络共享已开启"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫游时可能会产生额外的费用"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml deleted file mode 100644 index f378a9dc2cfb..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網絡共享連線至互聯網"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連接"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網絡共享"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"熱點或網絡共享已開啟"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫遊時可能需要支付額外費用"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml deleted file mode 100644 index cd653df1dac6..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網路共用連上網際網路"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連線"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網路共用"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"無線基地台或網路共用已開啟"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"使用漫遊服務可能須支付額外費用"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml deleted file mode 100644 index 32f6df56f154..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="no_upstream_notification_title" msgid="611650570559011140">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string> - <string name="no_upstream_notification_message" msgid="6508394877641864863">"Amadivayisi awakwazi ukuxhumeka"</string> - <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vala ukusebenzisa ifoni njengemodemu"</string> - <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string> - <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string> -</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml deleted file mode 100644 index 5c5be0466a36..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480/config.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to - "0" for disable this feature. --> - <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer> - - <!-- Config for showing upstream roaming notification. --> - <bool name="config_upstream_roaming_notification">true</bool> -</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mcc311-mnc480/strings.xml b/packages/Tethering/res/values-mcc311-mnc480/strings.xml deleted file mode 100644 index ce9ff6080717..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc480/strings.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_title">Tethering has no internet</string> - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_message">Devices can\u2019t connect</string> - <!-- String for no upstream notification disable button [CHAR LIMIT=200] --> - <string name="no_upstream_notification_disable_button">Turn off tethering</string> - - <!-- String for cellular roaming notification title [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string> - <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> - <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string> -</resources> diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml deleted file mode 100644 index 9ad9b9a58935..000000000000 --- a/packages/Tethering/res/values-mk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Активно е врзување или точка на пристап"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Допрете за поставување."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Врзувањето е оневозможено"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Контактирајте со администраторот за детали"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус на точката на пристап и врзувањето"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml deleted file mode 100644 index 9db79ce220a4..000000000000 --- a/packages/Tethering/res/values-ml/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്പോട്ട് സജീവമാണ്"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ഹോട്ട്സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml deleted file mode 100644 index 42d1edbaceb9..000000000000 --- a/packages/Tethering/res/values-mn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Модем болгох эсвэл сүлжээний цэг идэвхтэй байна"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Тохируулахын тулд товшино уу."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Модем болгохыг идэвхгүй болгосон"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Сүлжээний цэг болон модем болгох төлөв"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml deleted file mode 100644 index 13995b6b8aa5..000000000000 --- a/packages/Tethering/res/values-mr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग किंवा हॉटस्पॉट अॅक्टिव्ह आहे"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"सेट करण्यासाठी टॅप करा."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद केले आहे"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट आणि टेदरिंगची स्थिती"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml deleted file mode 100644 index d6a67f37b1de..000000000000 --- a/packages/Tethering/res/values-ms/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Penambatan atau tempat liputan aktif"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Ketik untuk membuat persediaan."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Penambatan dilumpuhkan"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi pentadbir anda untuk mendapatkan maklumat lanjut"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status tempat liputan & penambatan"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml deleted file mode 100644 index 49f6b88a7514..000000000000 --- a/packages/Tethering/res/values-my/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml deleted file mode 100644 index 9594e0a70a69..000000000000 --- a/packages/Tethering/res/values-nb/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Internettdeling eller Wi-Fi-sone er aktiv"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Trykk for å konfigurere."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internettdeling er slått av"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ta kontakt med administratoren din for å få mer informasjon"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for Wi-Fi-sone og internettdeling"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml deleted file mode 100644 index 72ae3a80a928..000000000000 --- a/packages/Tethering/res/values-ne/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिङ वा हटस्पट सक्रिय छ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"सेटअप गर्न ट्याप गर्नुहोस्।"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिङ सुविधा असक्षम पारिएको छ"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हटस्पट तथा टेदरिङको स्थिति"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml deleted file mode 100644 index 18b2bbfc7670..000000000000 --- a/packages/Tethering/res/values-nl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering of hotspot actief"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tik om in te stellen."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is uitgeschakeld"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Neem contact op met je beheerder voor meer informatie"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status van hotspot en tethering"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml deleted file mode 100644 index a15a6db42af6..000000000000 --- a/packages/Tethering/res/values-or/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml deleted file mode 100644 index a8235e423e47..000000000000 --- a/packages/Tethering/res/values-pa/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml deleted file mode 100644 index ccb017d43fa8..000000000000 --- a/packages/Tethering/res/values-pl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Aktywny tethering lub punkt dostępu"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Kliknij, by skonfigurować"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering został wyłączony"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot i tethering – stan"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml deleted file mode 100644 index a0a4745f9394..000000000000 --- a/packages/Tethering/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml deleted file mode 100644 index e3f03fcc6934..000000000000 --- a/packages/Tethering/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"A ligação (à Internet) via telemóvel está desativada."</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacte o administrador para obter detalhes."</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml deleted file mode 100644 index a0a4745f9394..000000000000 --- a/packages/Tethering/res/values-pt/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml deleted file mode 100644 index 5706a4a69c79..000000000000 --- a/packages/Tethering/res/values-ro/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering sau hotspot activ"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Atingeți ca să configurați."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tetheringul este dezactivat"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contactați administratorul pentru detalii"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Starea hotspotului și a tetheringului"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml deleted file mode 100644 index 7cb6f7db3fc8..000000000000 --- a/packages/Tethering/res/values-ru/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Включен режим модема или точка доступа"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Нажмите, чтобы настроить."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Использование телефона в качестве модема запрещено"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Чтобы узнать подробности, обратитесь к администратору."</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хот-спота и режима модема"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml deleted file mode 100644 index ec34c22de750..000000000000 --- a/packages/Tethering/res/values-si/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ටෙදරින් හෝ හොට්ස්පොට් සක්රීයයි"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"පිහිටුවීමට තට්ටු කරන්න."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ටෙදරින් අබල කර ඇත"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"හොට්ස්පොට් & ටෙදරින් තත්ත්වය"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml deleted file mode 100644 index 43e787c84f87..000000000000 --- a/packages/Tethering/res/values-sk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering alebo prístupový bod je aktívny"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím prejdete na nastavenie."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je deaktivovaný"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požiadajte svojho správcu"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml deleted file mode 100644 index 59433626a115..000000000000 --- a/packages/Tethering/res/values-sl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Dotaknite se, če želite nastaviti."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezava z internetom prek mobilnega telefona je onemogočena"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Za podrobnosti se obrnite na skrbnika"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stanje dostopne točke in povezave z internetom prek mobilnega telefona"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml deleted file mode 100644 index 21e11558bb0b..000000000000 --- a/packages/Tethering/res/values-sq/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Ndarja e internetit ose zona e qasjes së internetit është aktive"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Trokit për ta konfiguruar."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ndarja e internetit është çaktivizuar"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakto me administratorin për detaje"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Statusi i zonës së qasjes dhe ndarjes së internetit"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml deleted file mode 100644 index e2e4dc6361d4..000000000000 --- a/packages/Tethering/res/values-sr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Привезивање или хотспот је активан"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Додирните да бисте подесили."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Привезивање је онемогућено"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Потражите детаље од администратора"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хотспота и привезивања"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml deleted file mode 100644 index 72702c28587d..000000000000 --- a/packages/Tethering/res/values-sv/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Internetdelning eller surfzon har aktiverats"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Tryck om du vill konfigurera."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internetdelning har inaktiverats"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakta administratören om du vill veta mer"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trådlös surfzon och internetdelning har inaktiverats"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml deleted file mode 100644 index 65e4aa8cebb0..000000000000 --- a/packages/Tethering/res/values-sw/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Kusambaza mtandao au mtandaopepe umewashwa"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Gusa ili uweke mipangilio."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Umezima kipengele cha kusambaza mtandao"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Mtandaopepe na hali ya kusambaza mtandao"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml deleted file mode 100644 index 4aba62d4ab46..000000000000 --- a/packages/Tethering/res/values-ta/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"அமைக்க, தட்டவும்."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"டெதெரிங் முடக்கப்பட்டுள்ளது"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ஹாட்ஸ்பாட் & டெதெரிங் நிலை"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml deleted file mode 100644 index 1f917913416f..000000000000 --- a/packages/Tethering/res/values-te/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"టెథరింగ్ లేదా హాట్స్పాట్ యాక్టివ్గా ఉంది"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"సెటప్ చేయడానికి ట్యాప్ చేయండి."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"టెథరింగ్ డిజేబుల్ చేయబడింది"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"వివరాల కోసం మీ అడ్మిన్ని సంప్రదించండి"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"హాట్స్పాట్ & టెథరింగ్ స్థితి"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml deleted file mode 100644 index 44171c0db82f..000000000000 --- a/packages/Tethering/res/values-th/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"แตะเพื่อตั้งค่า"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml deleted file mode 100644 index 7347dd3e6254..000000000000 --- a/packages/Tethering/res/values-tl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Aktibo ang pag-tether o hotspot"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"I-tap para i-set up."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Naka-disable ang pag-tether"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status ng hotspot at pag-tether"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml deleted file mode 100644 index 32030f176574..000000000000 --- a/packages/Tethering/res/values-tr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering veya hotspot etkin"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamak için dokunun."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering devre dışı bırakıldı"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ayrıntılı bilgi için yöneticinize başvurun"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot ve tethering durumu"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml deleted file mode 100644 index 1ca89b3f7813..000000000000 --- a/packages/Tethering/res/values-uk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Модем чи точка доступу активні"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Натисніть, щоб налаштувати."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Використання телефона як модема вимкнено"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Щоб дізнатися більше, зв\'яжіться з адміністратором"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус точки доступу та модема"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml deleted file mode 100644 index d72c7d419577..000000000000 --- a/packages/Tethering/res/values-ur/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"ٹیدرنگ غیر فعال ہے"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml deleted file mode 100644 index af3b2ebb3500..000000000000 --- a/packages/Tethering/res/values-uz/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Modem rejimi yoki hotspot yoniq"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Sozlash uchun bosing."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modem rejimi faolsizlantirildi"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Tafsilotlari uchun administratoringizga murojaat qiling"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot va modem rejimi holati"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml deleted file mode 100644 index 21a0735922c3..000000000000 --- a/packages/Tethering/res/values-vi/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Tính năng chia sẻ Internet hoặc điểm phát sóng đang hoạt động"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Hãy nhấn để thiết lập."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Đã tắt tính năng chia sẻ Internet"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trạng thái điểm phát sóng và chia sẻ Internet"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml deleted file mode 100644 index 98e3b4b46fdb..000000000000 --- a/packages/Tethering/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"网络共享或热点已启用"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"点按即可设置。"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"网络共享已停用"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"如需了解详情,请与您的管理员联系"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"热点和网络共享状态"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml deleted file mode 100644 index 9cafd42dd43f..000000000000 --- a/packages/Tethering/res/values-zh-rHK/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"網絡共享或熱點已啟用"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"輕按即可設定。"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"網絡共享已停用"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"請聯絡您的管理員以瞭解詳情"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"熱點和網絡共享狀態"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml deleted file mode 100644 index 50a50bf7a996..000000000000 --- a/packages/Tethering/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"網路共用或無線基地台已啟用"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"輕觸即可進行設定。"</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"網路共用已停用"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳情請洽你的管理員"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"無線基地台與網路共用狀態"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml deleted file mode 100644 index f210f8726ee5..000000000000 --- a/packages/Tethering/res/values-zu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="6426563586025792944">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string> - <string name="tethered_notification_message" msgid="64800879503420696">"Thepha ukuze usethe."</string> - <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string> - <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string> - <string name="notification_channel_tethering_status" msgid="2663463891530932727">"I-Hotspot nesimo sokusebenzisa ifoni njengemodemu"</string> - <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> - <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> - <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> - <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> - <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> -</resources> diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml deleted file mode 100644 index 5f8d2997197f..000000000000 --- a/packages/Tethering/res/values/config.xml +++ /dev/null @@ -1,194 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- - OEMs that wish to change the below settings must do so via a runtime resource overlay package - and *NOT* by changing this file. This file is part of the tethering mainline module. - TODO: define two resources for each config item: a default_* resource and a config_* resource, - config_* is empty by default but may be overridden by RROs. - --> - <!-- List of regexpressions describing the interface (if any) that represent tetherable - USB interfaces. If the device doesn't want to support tethering over USB this should - be empty. An example would be "usb.*" --> - <string-array translatable="false" name="config_tether_usb_regexs"> - <item>"usb\\d"</item> - <item>"rndis\\d"</item> - </string-array> - - <!-- List of regexpressions describing the interface (if any) that represent tetherable - NCM interfaces. If the device doesn't want to support tethering over NCM this should - be empty. --> - <string-array translatable="false" name="config_tether_ncm_regexs"> - </string-array> - - <!-- List of regexpressions describing the interface (if any) that represent tetherable - Wifi interfaces. If the device doesn't want to support tethering over Wifi this - should be empty. An example would be "softap.*" --> - <string-array translatable="false" name="config_tether_wifi_regexs"> - <item>"wlan\\d"</item> - <item>"softap\\d"</item> - </string-array> - - <!-- List of regexpressions describing the interface (if any) that represent tetherable - WiGig interfaces. If the device doesn't want to support tethering over WiGig this - should be empty. An example would be "wigig\\d" --> - <string-array translatable="false" name="config_tether_wigig_regexs"> - <item>"wigig\\d"</item> - </string-array> - - <!-- List of regexpressions describing the interface (if any) that represent tetherable - Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this - should be empty. An example would be "p2p-p2p\\d-.*" --> - <string-array translatable="false" name="config_tether_wifi_p2p_regexs"> - <item>"p2p-p2p\\d-.*"</item> - <item>"p2p\\d"</item> - </string-array> - - <!-- List of regexpressions describing the interface (if any) that represent tetherable - bluetooth interfaces. If the device doesn't want to support tethering over bluetooth this - should be empty. --> - <string-array translatable="false" name="config_tether_bluetooth_regexs"> - <item>"bt-pan"</item> - </string-array> - - <!-- Use the BPF offload for tethering when the kernel has support. True by default. - If the device doesn't want to support tether BPF offload, this should be false. - Note that this setting could be overridden by device config. - --> - <bool translatable="false" name="config_tether_enable_bpf_offload">true</bool> - - <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. --> - <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool> - - <!-- Use legacy wifi p2p dedicated address instead of randomize address. --> - <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool> - - <!-- Dhcp range (min, max) to use for tethering purposes --> - <string-array translatable="false" name="config_tether_dhcp_range"> - </string-array> - - <!-- Used to config periodic polls tether offload stats from tethering offload HAL to make the - data warnings work. 5000(ms) by default. If the device doesn't want to poll tether - offload stats, this should be -1. Note that this setting could be override by - runtime resource overlays. - --> - <integer translatable="false" name="config_tether_offload_poll_interval">5000</integer> - - <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI, - WIFI} values allowable for tethering. - - Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or - [1,7,0] for TYPE_WIFI, TYPE_BLUETOOTH, and TYPE_MOBILE. - - This list is also modified by code within the framework, including: - - - TYPE_ETHERNET (9) is prepended to this list, and - - - the return value of TelephonyManager.isTetheringApnRequired() - determines how the array is further modified: - - * TRUE (DUN REQUIRED). - TYPE_MOBILE is removed (if present). - TYPE_MOBILE_HIPRI is removed (if present). - TYPE_MOBILE_DUN is appended (if not already present). - - * FALSE (DUN NOT REQUIRED). - TYPE_MOBILE_DUN is removed (if present). - If both of TYPE_MOBILE{,_HIPRI} are not present: - TYPE_MOBILE is appended. - TYPE_MOBILE_HIPRI is appended. - - For other changes applied to this list, now and in the future, see - com.android.networkstack.tethering.TetheringConfiguration. - - Note also: the order of this is important. The first upstream type - for which a satisfying network exists is used. - --> - <integer-array translatable="false" name="config_tether_upstream_types"> - </integer-array> - - <!-- When true, the tethering upstream network follows the current default - Internet network (except when the current default network is mobile, - in which case a DUN network will be used if required). - - When true, overrides the config_tether_upstream_types setting above. - --> - <bool translatable="false" name="config_tether_upstream_automatic">true</bool> - - - <!-- If the mobile hotspot feature requires provisioning, a package name and class name - can be provided to launch a supported application that provisions the devices. - EntitlementManager will send an intent to Settings with the specified package name and - class name in extras to launch provision app. - TODO: note what extras here. - - See EntitlementManager#runUiTetherProvisioning and - packages/apps/Settings/src/com/android/settings/network/TetherProvisioningActivity.java - for more details. - - For ui-less/periodic recheck support see config_mobile_hotspot_provision_app_no_ui - --> - <!-- The first element is the package name and the second element is the class name - of the provisioning app --> - <string-array translatable="false" name="config_mobile_hotspot_provision_app"> - <!-- - <item>com.example.provisioning</item> - <item>com.example.provisioning.Activity</item> - --> - </string-array> - - <!-- If the mobile hotspot feature requires provisioning, an action can be provided - that will be broadcast in non-ui cases for checking the provisioning status. - EntitlementManager will pass the specified name to Settings and Settings would - launch provisioning app by sending an intent with the package name. - - A second broadcast, action defined by config_mobile_hotspot_provision_response, - will be sent back to notify if provisioning succeeded or not. The response will - match that of the activity in config_mobile_hotspot_provision_app, but instead - contained within the int extra "EntitlementResult". - TODO: provide the system api for "EntitlementResult" extra and note it here. - - See EntitlementManager#runSilentTetherProvisioning and - packages/apps/Settings/src/com/android/settings/wifi/tether/TetherService.java for more - details. - --> - <string translatable="false" name="config_mobile_hotspot_provision_app_no_ui"></string> - - <!-- Sent in response to a provisioning check. The caller must hold the - permission android.permission.TETHER_PRIVILEGED for Settings to - receive this response. - - See config_mobile_hotspot_provision_response - --> - <string translatable="false" name="config_mobile_hotspot_provision_response"></string> - - <!-- Number of hours between each background provisioning call --> - <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer> - - <!-- ComponentName of the service used to run no ui tether provisioning. --> - <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string> - - <!-- No upstream notification is shown when there is a downstream but no upstream that is able - to do the tethering. --> - <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to - "-1" for disable this feature. --> - <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer> - - <!-- Cellular roaming notification is shown when upstream is cellular network and in roaming - state. --> - <!-- Config for showing upstream roaming notification. --> - <bool name="config_upstream_roaming_notification">false</bool> -</resources> diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml deleted file mode 100644 index 0ee7a992ee20..000000000000 --- a/packages/Tethering/res/values/overlayable.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources xmlns:android="http://schemas.android.com/apk/res/android"> - <overlayable name="TetheringConfig"> - <policy type="product|system|vendor"> - <!-- Params from config.xml that can be overlaid --> - <item type="array" name="config_tether_usb_regexs"/> - <item type="array" name="config_tether_ncm_regexs" /> - <item type="array" name="config_tether_wifi_regexs"/> - <item type="array" name="config_tether_wigig_regexs"/> - <item type="array" name="config_tether_wifi_p2p_regexs"/> - <item type="array" name="config_tether_bluetooth_regexs"/> - <item type="array" name="config_tether_dhcp_range"/> - <!-- Use the BPF offload for tethering when the kernel has support. True by default. - If the device doesn't want to support tether BPF offload, this should be false. - Note that this setting could be overridden by device config. - --> - <item type="bool" name="config_tether_enable_bpf_offload"/> - <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> - <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/> - <item type="integer" name="config_tether_offload_poll_interval"/> - <item type="array" name="config_tether_upstream_types"/> - <item type="bool" name="config_tether_upstream_automatic"/> - <!-- Configuration values for tethering entitlement check --> - <item type="array" name="config_mobile_hotspot_provision_app"/> - <item type="string" name="config_mobile_hotspot_provision_app_no_ui"/> - <item type="string" name="config_mobile_hotspot_provision_response"/> - <item type="integer" name="config_mobile_hotspot_provision_check_period"/> - <item type="string" name="config_wifi_tether_enable"/> - <!-- Params from config.xml that can be overlaid --> - </policy> - </overlayable> -</resources> diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml deleted file mode 100644 index d63c7c5063cc..000000000000 --- a/packages/Tethering/res/values/strings.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Shown when the device is tethered --> - <!-- String for tethered notification title [CHAR LIMIT=200] --> - <string name="tethered_notification_title">Tethering or hotspot active</string> - <!-- String for tethered notification message [CHAR LIMIT=200] --> - <string name="tethered_notification_message">Tap to set up.</string> - - <!-- This notification is shown when tethering has been disabled on a user's device. - The device is managed by the user's employer. Tethering can't be turned on unless the - IT administrator allows it. The noun "admin" is another reference for "IT administrator." --> - <!-- String for tether disabling notification title [CHAR LIMIT=200] --> - <string name="disable_tether_notification_title">Tethering is disabled</string> - <!-- String for tether disabling notification message [CHAR LIMIT=200] --> - <string name="disable_tether_notification_message">Contact your admin for details</string> - - <!-- This string should be consistent with the "Hotspot & tethering" text in the "Network and - Internet" settings page. That is currently the tether_settings_title_all string. --> - <!-- String for tether notification channel name [CHAR LIMIT=200] --> - <string name="notification_channel_tethering_status">Hotspot & tethering status</string> - - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_title"></string> - <!-- String for no upstream notification message [CHAR LIMIT=200] --> - <string name="no_upstream_notification_message"></string> - <!-- String for no upstream notification disable button [CHAR LIMIT=200] --> - <string name="no_upstream_notification_disable_button"></string> - - <!-- String for cellular roaming notification title [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_title"></string> - <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> - <string name="upstream_roaming_notification_message"></string> -</resources> diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java deleted file mode 100644 index 9fda1257b4c9..000000000000 --- a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -/** - * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion(). - * @hide - */ -public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub { - /** - * Get the version of the aidl interface implemented by the callbacks. - */ - @Override - public int getInterfaceVersion() { - return IDhcpServerCallbacks.VERSION; - } - - @Override - public String getInterfaceHash() { - return IDhcpServerCallbacks.HASH; - } -} diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java deleted file mode 100644 index aaaec17bf922..000000000000 --- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH; - -import android.net.LinkAddress; -import android.util.ArraySet; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.net.Inet4Address; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -/** - * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building. - * - * <p>This utility class does not check for validity of the parameters: invalid parameters are - * reported by the receiving module when unparceling the parcel. - * - * @see DhcpServingParams - * @hide - */ -public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { - public static final int MTU_UNSET = 0; - - /** - * Set the server address and served prefix for the DHCP server. - * - * <p>This parameter is required. - */ - public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) { - this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress()); - this.serverAddrPrefixLength = serverAddr.getPrefixLength(); - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - * <p>Each router must be inside the served prefix. This may be an empty set, but it must - * always be set explicitly. - */ - public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) { - this.defaultRouters = toIntArray(defaultRouters); - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - * <p>Each router must be inside the served prefix. This may be an empty list of routers, - * but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) { - return setDefaultRouters(newArraySet(defaultRouters)); - } - - /** - * Convenience method to build the parameters with no default router. - * - * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address. - */ - public DhcpServingParamsParcelExt setNoDefaultRouter() { - return setDefaultRouters(); - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - * <p>This may be an empty set, but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) { - this.dnsServers = toIntArray(dnsServers); - return this; - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - * <p>This may be an empty list of servers, but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) { - return setDnsServers(newArraySet(dnsServers)); - } - - /** - * Convenience method to build the parameters with no DNS server. - * - * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address. - */ - public DhcpServingParamsParcelExt setNoDnsServer() { - return setDnsServers(); - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - * <p>This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) { - this.excludedAddrs = toIntArray(excludedAddrs); - return this; - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - * <p>This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { - return setExcludedAddrs(newArraySet(excludedAddrs)); - } - - /** - * Set the lease time for leases assigned by the DHCP server. - * - * <p>This parameter is required. - */ - public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { - this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; - return this; - } - - /** - * Set the link MTU to be advertised to DHCP clients. - * - * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter - * is optional and defaults to {@link #MTU_UNSET}. - */ - public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) { - this.linkMtu = linkMtu; - return this; - } - - /** - * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option. - * - * <p>If not set, the default value is false. - */ - public DhcpServingParamsParcelExt setMetered(boolean metered) { - this.metered = metered; - return this; - } - - /** - * Set the client address to tell DHCP server only offer this address. - * The client's prefix length is the same as server's. - * - * <p>If not set, the default value is null. - */ - public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) { - this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); - return this; - } - - /** - * Set whether the DHCP server should request a new prefix from IpServer when receiving - * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB - * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests. - * - * <p>If not set, the default value is false. - */ - public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) { - this.changePrefixOnDecline = changePrefixOnDecline; - return this; - } - - private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) { - int[] res = new int[addrs.size()]; - int i = 0; - for (Inet4Address addr : addrs) { - res[i] = inet4AddressToIntHTH(addr); - i++; - } - return res; - } - - private static ArraySet<Inet4Address> newArraySet(Inet4Address... addrs) { - ArraySet<Inet4Address> addrSet = new ArraySet<>(addrs.length); - Collections.addAll(addrSet, addrs); - return addrSet; - } -} diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java deleted file mode 100644 index e2976b78908c..000000000000 --- a/packages/Tethering/src/android/net/ip/DadProxy.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.net.util.InterfaceParams; -import android.os.Handler; - -import androidx.annotation.VisibleForTesting; - -/** - * Basic Duplicate address detection proxy. - * - * @hide - */ -public class DadProxy { - private static final String TAG = DadProxy.class.getSimpleName(); - - @VisibleForTesting - public static NeighborPacketForwarder naForwarder; - public static NeighborPacketForwarder nsForwarder; - - public DadProxy(Handler h, InterfaceParams tetheredIface) { - naForwarder = new NeighborPacketForwarder(h, tetheredIface, - NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); - nsForwarder = new NeighborPacketForwarder(h, tetheredIface, - NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); - } - - /** Stop NS/NA Forwarders. */ - public void stop() { - naForwarder.stop(); - nsForwarder.stop(); - } - - /** Set upstream iface on both forwarders. */ - public void setUpstreamIface(InterfaceParams upstreamIface) { - naForwarder.setUpstreamIface(upstreamIface); - nsForwarder.setUpstreamIface(upstreamIface); - } -} diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java deleted file mode 100644 index 52d59fcdc19b..000000000000 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ /dev/null @@ -1,1422 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; -import static android.net.util.NetworkConstants.asByte; -import static android.net.util.PrefixUtils.asIpPrefix; -import static android.net.util.TetheringMessageBase.BASE_IPSERVER; -import static android.system.OsConstants.RT_SCOPE_UNIVERSE; - -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; - -import android.net.INetd; -import android.net.INetworkStackStatusCallback; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.RouteInfo; -import android.net.TetheredClient; -import android.net.TetheringManager; -import android.net.TetheringRequestParcel; -import android.net.dhcp.DhcpLeaseParcelable; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.DhcpServingParamsParcelExt; -import android.net.dhcp.IDhcpEventCallbacks; -import android.net.dhcp.IDhcpServer; -import android.net.ip.IpNeighborMonitor.NeighborEvent; -import android.net.ip.RouterAdvertisementDaemon.RaParams; -import android.net.shared.NetdUtils; -import android.net.shared.RouteUtils; -import android.net.util.InterfaceParams; -import android.net.util.InterfaceSet; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.util.MessageUtils; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.networkstack.tethering.BpfCoordinator; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; -import com.android.networkstack.tethering.PrivateAddressCoordinator; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.NetworkInterface; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Random; -import java.util.Set; - -/** - * Provides the interface to IP-layer serving functionality for a given network - * interface, e.g. for tethering or "local-only hotspot" mode. - * - * @hide - */ -public class IpServer extends StateMachine { - public static final int STATE_UNAVAILABLE = 0; - public static final int STATE_AVAILABLE = 1; - public static final int STATE_TETHERED = 2; - public static final int STATE_LOCAL_ONLY = 3; - - /** Get string name of |state|.*/ - public static String getStateString(int state) { - switch (state) { - case STATE_UNAVAILABLE: return "UNAVAILABLE"; - case STATE_AVAILABLE: return "AVAILABLE"; - case STATE_TETHERED: return "TETHERED"; - case STATE_LOCAL_ONLY: return "LOCAL_ONLY"; - } - return "UNKNOWN: " + state; - } - - private static final byte DOUG_ADAMS = (byte) 42; - - // TODO: have PanService use some visible version of this constant - private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24"; - - // TODO: have this configurable - private static final int DHCP_LEASE_TIME_SECS = 3600; - - private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00"); - - private static final String TAG = "IpServer"; - private static final boolean DBG = false; - private static final boolean VDBG = false; - private static final Class[] sMessageClasses = { - IpServer.class - }; - private static final SparseArray<String> sMagicDecoderRing = - MessageUtils.findMessageNames(sMessageClasses); - - /** IpServer callback. */ - public static class Callback { - /** - * Notify that |who| has changed its tethering state. - * - * @param who the calling instance of IpServer - * @param state one of STATE_* - * @param lastError one of TetheringManager.TETHER_ERROR_* - */ - public void updateInterfaceState(IpServer who, int state, int lastError) { } - - /** - * Notify that |who| has new LinkProperties. - * - * @param who the calling instance of IpServer - * @param newLp the new LinkProperties to report - */ - public void updateLinkProperties(IpServer who, LinkProperties newLp) { } - - /** - * Notify that the DHCP leases changed in one of the IpServers. - */ - public void dhcpLeasesChanged() { } - - /** - * Request Tethering change. - * - * @param tetheringType the downstream type of this IpServer. - * @param enabled enable or disable tethering. - */ - public void requestEnableTethering(int tetheringType, boolean enabled) { } - } - - /** Capture IpServer dependencies, for injection. */ - public abstract static class Dependencies { - /** - * Create a DadProxy instance to be used by IpServer. - * To support multiple tethered interfaces concurrently DAD Proxy - * needs to be supported per IpServer instead of per upstream. - */ - public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) { - return new DadProxy(handler, ifParams); - } - - /** Create an IpNeighborMonitor to be used by this IpServer */ - public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, - IpNeighborMonitor.NeighborEventConsumer consumer) { - return new IpNeighborMonitor(handler, log, consumer); - } - - /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/ - public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { - return new RouterAdvertisementDaemon(ifParams); - } - - /** Get |ifName|'s interface information.*/ - public InterfaceParams getInterfaceParams(String ifName) { - return InterfaceParams.getByName(ifName); - } - - /** Get |ifName|'s interface index. */ - public int getIfindex(String ifName) { - try { - return NetworkInterface.getByName(ifName).getIndex(); - } catch (IOException | NullPointerException e) { - Log.e(TAG, "Can't determine interface index for interface " + ifName); - return 0; - } - } - - /** Create a DhcpServer instance to be used by IpServer. */ - public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb); - } - - // request from the user that it wants to tether - public static final int CMD_TETHER_REQUESTED = BASE_IPSERVER + 1; - // request from the user that it wants to untether - public static final int CMD_TETHER_UNREQUESTED = BASE_IPSERVER + 2; - // notification that this interface is down - public static final int CMD_INTERFACE_DOWN = BASE_IPSERVER + 3; - // notification from the {@link Tethering.TetherMainSM} that it had trouble enabling IP - // Forwarding - public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IPSERVER + 4; - // notification from the {@link Tethering.TetherMainSM} SM that it had trouble disabling IP - // Forwarding - public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5; - // notification from the {@link Tethering.TetherMainSM} SM that it had trouble starting - // tethering - public static final int CMD_START_TETHERING_ERROR = BASE_IPSERVER + 6; - // notification from the {@link Tethering.TetherMainSM} that it had trouble stopping tethering - public static final int CMD_STOP_TETHERING_ERROR = BASE_IPSERVER + 7; - // notification from the {@link Tethering.TetherMainSM} that it had trouble setting the DNS - // forwarders - public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IPSERVER + 8; - // the upstream connection has changed - public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9; - // new IPv6 tethering parameters need to be processed - public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; - // new neighbor cache entry on our interface - public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11; - // request from DHCP server that it wants to have a new prefix - public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12; - // request from PrivateAddressCoordinator to restart tethering. - public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13; - - private final State mInitialState; - private final State mLocalHotspotState; - private final State mTetheredState; - private final State mUnavailableState; - private final State mWaitingForRestartState; - - private final SharedLog mLog; - private final INetd mNetd; - @NonNull - private final BpfCoordinator mBpfCoordinator; - private final Callback mCallback; - private final InterfaceController mInterfaceCtrl; - private final PrivateAddressCoordinator mPrivateAddressCoordinator; - - private final String mIfaceName; - private final int mInterfaceType; - private final LinkProperties mLinkProperties; - private final boolean mUsingLegacyDhcp; - private final boolean mUsingBpfOffload; - - private final Dependencies mDeps; - - private int mLastError; - private int mServingMode; - private InterfaceSet mUpstreamIfaceSet; // may change over time - private InterfaceParams mInterfaceParams; - // TODO: De-duplicate this with mLinkProperties above. Currently, these link - // properties are those selected by the IPv6TetheringCoordinator and relayed - // to us. By comparison, mLinkProperties contains the addresses and directly - // connected routes that have been formed from these properties iff. we have - // succeeded in configuring them and are able to announce them within Router - // Advertisements (otherwise, we do not add them to mLinkProperties at all). - private LinkProperties mLastIPv6LinkProperties; - private RouterAdvertisementDaemon mRaDaemon; - private DadProxy mDadProxy; - - // To be accessed only on the handler thread - private int mDhcpServerStartIndex = 0; - private IDhcpServer mDhcpServer; - private RaParams mLastRaParams; - - private LinkAddress mStaticIpv4ServerAddr; - private LinkAddress mStaticIpv4ClientAddr; - - @NonNull - private List<TetheredClient> mDhcpLeases = Collections.emptyList(); - - private int mLastIPv6UpstreamIfindex = 0; - - private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer { - public void accept(NeighborEvent e) { - sendMessage(CMD_NEIGHBOR_EVENT, e); - } - } - - private final IpNeighborMonitor mIpNeighborMonitor; - - private LinkAddress mIpv4Address; - - // TODO: Add a dependency object to pass the data members or variables from the tethering - // object. It helps to reduce the arguments of the constructor. - public IpServer( - String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetd netd, @NonNull BpfCoordinator coordinator, Callback callback, - boolean usingLegacyDhcp, boolean usingBpfOffload, - PrivateAddressCoordinator addressCoordinator, Dependencies deps) { - super(ifaceName, looper); - mLog = log.forSubComponent(ifaceName); - mNetd = netd; - mBpfCoordinator = coordinator; - mCallback = callback; - mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); - mIfaceName = ifaceName; - mInterfaceType = interfaceType; - mLinkProperties = new LinkProperties(); - mUsingLegacyDhcp = usingLegacyDhcp; - mUsingBpfOffload = usingBpfOffload; - mPrivateAddressCoordinator = addressCoordinator; - mDeps = deps; - resetLinkProperties(); - mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; - mServingMode = STATE_AVAILABLE; - - mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, - new MyNeighborEventConsumer()); - - // IP neighbor monitor monitors the neighbor events for adding/removing offload - // forwarding rules per client. If BPF offload is not supported, don't start listening - // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule, - // removeIpv6ForwardingRule. - if (mUsingBpfOffload && !mIpNeighborMonitor.start()) { - mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); - } - - mInitialState = new InitialState(); - mLocalHotspotState = new LocalHotspotState(); - mTetheredState = new TetheredState(); - mUnavailableState = new UnavailableState(); - mWaitingForRestartState = new WaitingForRestartState(); - addState(mInitialState); - addState(mLocalHotspotState); - addState(mTetheredState); - addState(mWaitingForRestartState, mTetheredState); - addState(mUnavailableState); - - setInitialState(mInitialState); - } - - /** Interface name which IpServer served.*/ - public String interfaceName() { - return mIfaceName; - } - - /** - * Tethering downstream type. It would be one of TetheringManager#TETHERING_*. - */ - public int interfaceType() { - return mInterfaceType; - } - - /** Last error from this IpServer. */ - public int lastError() { - return mLastError; - } - - /** Serving mode is the current state of IpServer state machine. */ - public int servingMode() { - return mServingMode; - } - - /** The properties of the network link which IpServer is serving. */ - public LinkProperties linkProperties() { - return new LinkProperties(mLinkProperties); - } - - /** The address which IpServer is using. */ - public LinkAddress getAddress() { - return mIpv4Address; - } - - /** - * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper - * thread. - */ - public List<TetheredClient> getAllLeases() { - return Collections.unmodifiableList(mDhcpLeases); - } - - /** Stop this IpServer. After this is called this IpServer should not be used any more. */ - public void stop() { - sendMessage(CMD_INTERFACE_DOWN); - } - - /** - * Tethering is canceled. IpServer state machine will be available and wait for - * next tethering request. - */ - public void unwanted() { - sendMessage(CMD_TETHER_UNREQUESTED); - } - - /** Internals. */ - - private boolean startIPv4() { - return configureIPv4(true); - } - - /** - * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer - * handler. - * - * <p>Different instances of this class can be created for each call to IDhcpServer methods, - * with different implementations of the callback, to differentiate handling of success/error in - * each call. - */ - private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub { - @Override - public void onStatusAvailable(int statusCode) { - getHandler().post(() -> callback(statusCode)); - } - - public abstract void callback(int statusCode); - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() { - return this.HASH; - } - } - - private class DhcpServerCallbacksImpl extends DhcpServerCallbacks { - private final int mStartIndex; - - private DhcpServerCallbacksImpl(int startIndex) { - mStartIndex = startIndex; - } - - @Override - public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException { - getHandler().post(() -> { - // We are on the handler thread: mDhcpServerStartIndex can be read safely. - if (mStartIndex != mDhcpServerStartIndex) { - // This start request is obsolete. Explicitly stop the DHCP server to shut - // down its thread. When the |server| binder token goes out of scope, the - // garbage collector will finalize it, which causes the network stack process - // garbage collector to collect the server itself. - try { - server.stop(null); - } catch (RemoteException e) { } - return; - } - - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error obtaining DHCP server: " + statusCode); - handleError(); - return; - } - - mDhcpServer = server; - try { - mDhcpServer.startWithCallbacks(new OnHandlerStatusCallback() { - @Override - public void callback(int startStatusCode) { - if (startStatusCode != STATUS_SUCCESS) { - mLog.e("Error starting DHCP server: " + startStatusCode); - handleError(); - } - } - }, new DhcpEventCallback()); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - }); - } - - private void handleError() { - mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; - transitionTo(mInitialState); - } - } - - private class DhcpEventCallback extends IDhcpEventCallbacks.Stub { - @Override - public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) { - final ArrayList<TetheredClient> leases = new ArrayList<>(); - for (DhcpLeaseParcelable lease : leaseParcelables) { - final LinkAddress address = new LinkAddress( - intToInet4AddressHTH(lease.netAddr), lease.prefixLength, - 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */, - lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */); - - final MacAddress macAddress; - try { - macAddress = MacAddress.fromBytes(lease.hwAddr); - } catch (IllegalArgumentException e) { - Log.wtf(TAG, "Invalid address received from DhcpServer: " - + Arrays.toString(lease.hwAddr)); - return; - } - - final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo( - address, lease.hostname); - leases.add(new TetheredClient( - macAddress, - Collections.singletonList(addressInfo), - mInterfaceType)); - } - - getHandler().post(() -> { - mDhcpLeases = leases; - mCallback.dhcpLeasesChanged(); - }); - } - - @Override - public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { - Objects.requireNonNull(currentPrefix); - sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() throws RemoteException { - return this.HASH; - } - } - - private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) { - Objects.requireNonNull(ipv4Address); - return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST); - } - - private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter, - @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr, - @Nullable Inet4Address clientAddr) { - final boolean changePrefixOnDecline = - (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null); - return new DhcpServingParamsParcelExt() - .setDefaultRouters(defaultRouter) - .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS) - .setDnsServers(dnsServer) - .setServerAddr(serverAddr) - .setMetered(true) - .setSingleClientAddr(clientAddr) - .setChangePrefixOnDecline(changePrefixOnDecline); - // TODO: also advertise link MTU - } - - private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) { - if (mUsingLegacyDhcp) { - return true; - } - - final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress(); - final Inet4Address clientAddr = clientLinkAddr == null ? null : - (Inet4Address) clientLinkAddr.getAddress(); - - final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */, - addr /* dnsServer */, serverLinkAddr, clientAddr); - mDhcpServerStartIndex++; - mDeps.makeDhcpServer( - mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex)); - return true; - } - - private void stopDhcp() { - // Make all previous start requests obsolete so servers are not started later - mDhcpServerStartIndex++; - - if (mDhcpServer != null) { - try { - mDhcpServer.stop(new OnHandlerStatusCallback() { - @Override - public void callback(int statusCode) { - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error stopping DHCP server: " + statusCode); - mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; - // Not much more we can do here - } - mDhcpLeases.clear(); - getHandler().post(mCallback::dhcpLeasesChanged); - } - }); - mDhcpServer = null; - } catch (RemoteException e) { - mLog.e("Error stopping DHCP server", e); - // Not much more we can do here - } - } - } - - private boolean configureDhcp(boolean enable, final LinkAddress serverAddr, - final LinkAddress clientAddr) { - if (enable) { - return startDhcp(serverAddr, clientAddr); - } else { - stopDhcp(); - return true; - } - } - - private void stopIPv4() { - configureIPv4(false); - // NOTE: All of configureIPv4() will be refactored out of existence - // into calls to InterfaceController, shared with startIPv4(). - mInterfaceCtrl.clearIPv4Address(); - mPrivateAddressCoordinator.releaseDownstream(this); - mIpv4Address = null; - mStaticIpv4ServerAddr = null; - mStaticIpv4ClientAddr = null; - } - - private boolean configureIPv4(boolean enabled) { - if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); - - if (enabled) { - mIpv4Address = requestIpv4Address(true /* useLastAddress */); - } - - if (mIpv4Address == null) { - mLog.e("No available ipv4 address"); - return false; - } - - if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) { - // BT configures the interface elsewhere: only start DHCP. - // TODO: make all tethering types behave the same way, and delete the bluetooth - // code that calls into NetworkManagementService directly. - return configureDhcp(enabled, mIpv4Address, null /* clientAddress */); - } - - final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address); - - final Boolean setIfaceUp; - if (mInterfaceType == TetheringManager.TETHERING_WIFI - || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P - || mInterfaceType == TetheringManager.TETHERING_ETHERNET - || mInterfaceType == TetheringManager.TETHERING_WIGIG) { - // The WiFi and Ethernet stack has ownership of the interface up/down state. - // It is unclear whether the Bluetooth or USB stacks will manage their own - // state. - setIfaceUp = null; - } else { - setIfaceUp = enabled; - } - if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) { - mLog.e("Error configuring interface"); - if (!enabled) stopDhcp(); - return false; - } - - if (enabled) { - mLinkProperties.addLinkAddress(mIpv4Address); - mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address)); - } else { - mLinkProperties.removeLinkAddress(mIpv4Address); - mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address)); - } - return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr); - } - - private LinkAddress requestIpv4Address(final boolean useLastAddress) { - if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr; - - if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) { - return new LinkAddress(BLUETOOTH_IFACE_ADDR); - } - - return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress); - } - - private boolean startIPv6() { - mInterfaceParams = mDeps.getInterfaceParams(mIfaceName); - if (mInterfaceParams == null) { - mLog.e("Failed to find InterfaceParams"); - stopIPv6(); - return false; - } - - mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams); - if (!mRaDaemon.start()) { - stopIPv6(); - return false; - } - - // TODO: use ShimUtils instead of explicitly checking the version here. - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME) - || "T".equals(Build.VERSION.CODENAME)) { - // DAD Proxy starts forwarding packets after IPv6 upstream is present. - mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams); - } - - return true; - } - - private void stopIPv6() { - mInterfaceParams = null; - setRaParams(null); - - if (mRaDaemon != null) { - mRaDaemon.stop(); - mRaDaemon = null; - } - - if (mDadProxy != null) { - mDadProxy.stop(); - mDadProxy = null; - } - } - - // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only - // LinkProperties. These have extraneous data filtered out and only the - // necessary prefixes included (per its prefix distribution policy). - // - // TODO: Evaluate using a data structure than is more directly suited to - // communicating only the relevant information. - private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) { - if (mRaDaemon == null) return; - - // Avoid unnecessary work on spurious updates. - if (Objects.equals(mLastIPv6LinkProperties, v6only)) { - return; - } - - RaParams params = null; - String upstreamIface = null; - InterfaceParams upstreamIfaceParams = null; - int upstreamIfIndex = 0; - - if (v6only != null) { - upstreamIface = v6only.getInterfaceName(); - upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface); - if (upstreamIfaceParams != null) { - upstreamIfIndex = upstreamIfaceParams.index; - } - params = new RaParams(); - params.mtu = v6only.getMtu(); - params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); - - if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); - - for (LinkAddress linkAddr : v6only.getLinkAddresses()) { - if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; - - final IpPrefix prefix = new IpPrefix( - linkAddr.getAddress(), linkAddr.getPrefixLength()); - params.prefixes.add(prefix); - - final Inet6Address dnsServer = getLocalDnsIpFor(prefix); - if (dnsServer != null) { - params.dnses.add(dnsServer); - } - } - - // Add upstream index to name mapping for the tether stats usage in the coordinator. - // Although this mapping could be added by both class Tethering and IpServer, adding - // mapping from IpServer guarantees that the mapping is added before the adding - // forwarding rules. That is because there are different state machines in both - // classes. It is hard to guarantee the link property update order between multiple - // state machines. - mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface); - } - - // If v6only is null, we pass in null to setRaParams(), which handles - // deprecation of any existing RA data. - - setRaParams(params); - mLastIPv6LinkProperties = v6only; - - updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null); - mLastIPv6UpstreamIfindex = upstreamIfIndex; - if (mDadProxy != null) { - mDadProxy.setUpstreamIface(upstreamIfaceParams); - } - } - - private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) { - final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork( - mNetd, toBeRemoved); - if (removalFailures > 0) { - mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", - removalFailures)); - } - - for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); - } - - private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) { - try { - // It's safe to call networkAddInterface() even if - // the interface is already in the local_network. - mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName); - try { - // Add routes from local network. Note that adding routes that - // already exist does not cause an error (EEXIST is silently ignored). - RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded); - } catch (IllegalStateException e) { - mLog.e("Failed to add IPv4/v6 routes to local table: " + e); - return; - } - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to add " + mIfaceName + " to local table: ", e); - return; - } - - for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); - } - - private void configureLocalIPv6Routes( - HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) { - // [1] Remove the routes that are deprecated. - if (!deprecatedPrefixes.isEmpty()) { - removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes)); - } - - // [2] Add only the routes that have not previously been added. - if (newPrefixes != null && !newPrefixes.isEmpty()) { - HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone(); - if (mLastRaParams != null) { - addedPrefixes.removeAll(mLastRaParams.prefixes); - } - - if (!addedPrefixes.isEmpty()) { - addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes)); - } - } - } - - private void configureLocalIPv6Dns( - HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) { - // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located? - if (mNetd == null) { - if (newDnses != null) newDnses.clear(); - mLog.e("No netd service instance available; not setting local IPv6 addresses"); - return; - } - - // [1] Remove deprecated local DNS IP addresses. - if (!deprecatedDnses.isEmpty()) { - for (Inet6Address dns : deprecatedDnses) { - if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) { - mLog.e("Failed to remove local dns IP " + dns); - } - - mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); - } - } - - // [2] Add only the local DNS IP addresses that have not previously been added. - if (newDnses != null && !newDnses.isEmpty()) { - final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone(); - if (mLastRaParams != null) { - addedDnses.removeAll(mLastRaParams.dnses); - } - - for (Inet6Address dns : addedDnses) { - if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) { - mLog.e("Failed to add local dns IP " + dns); - newDnses.remove(dns); - } - - mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); - } - } - - try { - mNetd.tetherApplyDnsInterfaces(); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to update local DNS caching server"); - if (newDnses != null) newDnses.clear(); - } - } - - private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { - // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF - // offload is disabled. Add this check just in case. - // TODO: Perhaps remove this protection check. - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleAdd(this, rule); - } - - private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) { - // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleRemove(this, rule); - } - - private void clearIpv6ForwardingRules() { - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleClear(this); - } - - private void updateIpv6ForwardingRule(int newIfindex) { - // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. - if (!mUsingBpfOffload) return; - - mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex); - } - - // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream - // changes or if a neighbor event is received. - private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex, - NeighborEvent e) { - // If we no longer have an upstream, clear forwarding rules and do nothing else. - if (upstreamIfindex == 0) { - clearIpv6ForwardingRules(); - return; - } - - // If the upstream interface has changed, remove all rules and re-add them with the new - // upstream interface. - if (prevUpstreamIfindex != upstreamIfindex) { - updateIpv6ForwardingRule(upstreamIfindex); - } - - // If we're here to process a NeighborEvent, do so now. - // mInterfaceParams must be non-null or the event would not have arrived. - if (e == null) return; - if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress() - || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) { - return; - } - - // When deleting rules, we still need to pass a non-null MAC, even though it's ignored. - // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never - // add rules with a null MAC, only delete them. - MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS; - Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex, - mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac); - if (e.isValid()) { - addIpv6ForwardingRule(rule); - } else { - removeIpv6ForwardingRule(rule); - } - } - - private void handleNeighborEvent(NeighborEvent e) { - if (mInterfaceParams != null - && mInterfaceParams.index == e.ifindex - && mInterfaceParams.hasMacAddress) { - updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e); - } - } - - private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) { - if (!currentPrefix.contains(mIpv4Address.getAddress()) - || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) { - Log.e(TAG, "Invalid prefix: " + currentPrefix); - return; - } - - final LinkAddress deprecatedLinkAddress = mIpv4Address; - mIpv4Address = requestIpv4Address(false); - if (mIpv4Address == null) { - mLog.e("Fail to request a new downstream prefix"); - return; - } - final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress(); - - // Add new IPv4 address on the interface. - if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) { - mLog.e("Failed to add new IP " + srvAddr); - return; - } - - // Remove deprecated routes from local network. - removeRoutesFromLocalNetwork( - Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress))); - mLinkProperties.removeLinkAddress(deprecatedLinkAddress); - - // Add new routes to local network. - addRoutesToLocalNetwork( - Collections.singletonList(getDirectConnectedRoute(mIpv4Address))); - mLinkProperties.addLinkAddress(mIpv4Address); - - // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't - // listen on the interface configured with new IPv4 address, that results DNS validation - // failure of downstream client even if appropriate routes have been configured. - try { - mNetd.tetherApplyDnsInterfaces(); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to update local DNS caching server"); - return; - } - sendLinkProperties(); - - // Notify DHCP server that new prefix/route has been applied on IpServer. - final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null : - (Inet4Address) mStaticIpv4ClientAddr.getAddress(); - final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */, - srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr); - try { - mDhcpServer.updateParams(params, new OnHandlerStatusCallback() { - @Override - public void callback(int statusCode) { - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error updating DHCP serving params: " + statusCode); - } - } - }); - } catch (RemoteException e) { - mLog.e("Error updating DHCP serving params", e); - } - } - - private byte getHopLimit(String upstreamIface, int adjustTTL) { - try { - int upstreamHopLimit = Integer.parseUnsignedInt( - mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit")); - upstreamHopLimit = upstreamHopLimit + adjustTTL; - // Cap the hop limit to 255. - return (byte) Integer.min(upstreamHopLimit, 255); - } catch (Exception e) { - mLog.e("Failed to find upstream interface hop limit", e); - } - return RaParams.DEFAULT_HOPLIMIT; - } - - private void setRaParams(RaParams newParams) { - if (mRaDaemon != null) { - final RaParams deprecatedParams = - RaParams.getDeprecatedRaParams(mLastRaParams, newParams); - - configureLocalIPv6Routes(deprecatedParams.prefixes, - (newParams != null) ? newParams.prefixes : null); - - configureLocalIPv6Dns(deprecatedParams.dnses, - (newParams != null) ? newParams.dnses : null); - - mRaDaemon.buildNewRa(deprecatedParams, newParams); - } - - mLastRaParams = newParams; - } - - private void logMessage(State state, int what) { - mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what))); - } - - private void sendInterfaceState(int newInterfaceState) { - mServingMode = newInterfaceState; - mCallback.updateInterfaceState(this, newInterfaceState, mLastError); - sendLinkProperties(); - } - - private void sendLinkProperties() { - mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties)); - } - - private void resetLinkProperties() { - mLinkProperties.clear(); - mLinkProperties.setInterfaceName(mIfaceName); - } - - private void maybeConfigureStaticIp(final TetheringRequestParcel request) { - // Ignore static address configuration if they are invalid or null. In theory, static - // addresses should not be invalid here because TetheringManager do not allow caller to - // specify invalid static address configuration. - if (request == null || request.localIPv4Address == null - || request.staticClientAddress == null || !checkStaticAddressConfiguration( - request.localIPv4Address, request.staticClientAddress)) { - return; - } - - mStaticIpv4ServerAddr = request.localIPv4Address; - mStaticIpv4ClientAddr = request.staticClientAddress; - } - - class InitialState extends State { - @Override - public void enter() { - sendInterfaceState(STATE_AVAILABLE); - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; - switch (message.arg1) { - case STATE_LOCAL_ONLY: - maybeConfigureStaticIp((TetheringRequestParcel) message.obj); - transitionTo(mLocalHotspotState); - break; - case STATE_TETHERED: - maybeConfigureStaticIp((TetheringRequestParcel) message.obj); - transitionTo(mTetheredState); - break; - default: - mLog.e("Invalid tethering interface serving state specified."); - } - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - break; - case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class BaseServingState extends State { - @Override - public void enter() { - if (!startIPv4()) { - mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; - return; - } - - try { - NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address)); - } catch (RemoteException | ServiceSpecificException | IllegalStateException e) { - mLog.e("Error Tethering", e); - mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; - return; - } - - if (!startIPv6()) { - mLog.e("Failed to startIPv6"); - // TODO: Make this a fatal error once Bluetooth IPv6 is sorted. - return; - } - } - - @Override - public void exit() { - // Note that at this point, we're leaving the tethered state. We can fail any - // of these operations, but it doesn't really change that we have to try them - // all in sequence. - stopIPv6(); - - try { - NetdUtils.untetherInterface(mNetd, mIfaceName); - } catch (RemoteException | ServiceSpecificException e) { - mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; - mLog.e("Failed to untether interface: " + e); - } - - stopIPv4(); - - resetLinkProperties(); - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_UNREQUESTED: - transitionTo(mInitialState); - if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName); - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); - break; - case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); - sendLinkProperties(); - break; - case CMD_IP_FORWARDING_ENABLE_ERROR: - case CMD_IP_FORWARDING_DISABLE_ERROR: - case CMD_START_TETHERING_ERROR: - case CMD_STOP_TETHERING_ERROR: - case CMD_SET_DNS_FORWARDERS_ERROR: - mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR; - transitionTo(mInitialState); - break; - case CMD_NEW_PREFIX_REQUEST: - handleNewPrefixRequest((IpPrefix) message.obj); - break; - case CMD_NOTIFY_PREFIX_CONFLICT: - mLog.i("restart tethering: " + mInterfaceType); - mCallback.requestEnableTethering(mInterfaceType, false /* enabled */); - transitionTo(mWaitingForRestartState); - break; - default: - return false; - } - return true; - } - } - - // Handling errors in BaseServingState.enter() by transitioning is - // problematic because transitioning during a multi-state jump yields - // a Log.wtf(). Ultimately, there should be only one ServingState, - // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of IpServer. - class LocalHotspotState extends BaseServingState { - @Override - public void enter() { - super.enter(); - if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) { - transitionTo(mInitialState); - } - - if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName); - sendInterfaceState(STATE_LOCAL_ONLY); - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message)) return true; - - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode."); - break; - case CMD_TETHER_CONNECTION_CHANGED: - // Ignored in local hotspot state. - break; - default: - return false; - } - return true; - } - } - - // Handling errors in BaseServingState.enter() by transitioning is - // problematic because transitioning during a multi-state jump yields - // a Log.wtf(). Ultimately, there should be only one ServingState, - // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of IpServer. - class TetheredState extends BaseServingState { - @Override - public void enter() { - super.enter(); - if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) { - transitionTo(mInitialState); - } - - if (DBG) Log.d(TAG, "Tethered " + mIfaceName); - sendInterfaceState(STATE_TETHERED); - } - - @Override - public void exit() { - cleanupUpstream(); - super.exit(); - } - - private void cleanupUpstream() { - if (mUpstreamIfaceSet == null) return; - - for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); - mUpstreamIfaceSet = null; - clearIpv6ForwardingRules(); - } - - private void cleanupUpstreamInterface(String upstreamIface) { - // Note that we don't care about errors here. - // Sometimes interfaces are gone before we get - // to remove their rules, which generates errors. - // Just do the best we can. - try { - mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString()); - } - try { - mNetd.tetherRemoveForward(mIfaceName, upstreamIface); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception in disableNat: " + e.toString()); - } - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message)) return true; - - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLog.e("CMD_TETHER_REQUESTED while already tethering."); - break; - case CMD_TETHER_CONNECTION_CHANGED: - final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj; - if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) { - if (VDBG) Log.d(TAG, "Connection changed noop - dropping"); - break; - } - - if (newUpstreamIfaceSet == null) { - cleanupUpstream(); - break; - } - - for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) { - cleanupUpstreamInterface(removed); - } - - final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet); - // This makes the call to cleanupUpstream() in the error - // path for any interface neatly cleanup all the interfaces. - mUpstreamIfaceSet = newUpstreamIfaceSet; - - for (String ifname : added) { - try { - mNetd.tetherAddForward(mIfaceName, ifname); - mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception enabling NAT: " + e.toString()); - cleanupUpstream(); - mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR; - transitionTo(mInitialState); - return true; - } - } - break; - case CMD_NEIGHBOR_EVENT: - handleNeighborEvent((NeighborEvent) message.obj); - break; - default: - return false; - } - return true; - } - - private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) { - if (mUpstreamIfaceSet == null && newIfaces == null) return true; - if (mUpstreamIfaceSet != null && newIfaces != null) { - return mUpstreamIfaceSet.equals(newIfaces); - } - return false; - } - - private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) { - if (mUpstreamIfaceSet == null) return new HashSet<>(); - - final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames); - removed.removeAll(newIfaces.ifnames); - return removed; - } - - private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) { - final HashSet<String> added = new HashSet<>(newIfaces.ifnames); - if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames); - return added; - } - } - - /** - * This state is terminal for the per interface state machine. At this - * point, the tethering main state machine should have removed this interface - * specific state machine from its list of possible recipients of - * tethering requests. The state machine itself will hang around until - * the garbage collector finds it. - */ - class UnavailableState extends State { - @Override - public void enter() { - mIpNeighborMonitor.stop(); - mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; - sendInterfaceState(STATE_UNAVAILABLE); - } - } - - class WaitingForRestartState extends State { - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_UNREQUESTED: - transitionTo(mInitialState); - mLog.i("Untethered (unrequested) and restarting " + mIfaceName); - mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - mLog.i("Untethered (interface down) and restarting" + mIfaceName); - mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); - break; - default: - return false; - } - return true; - } - } - - // Accumulate routes representing "prefixes to be assigned to the local - // interface", for subsequent modification of local_network routing. - private static ArrayList<RouteInfo> getLocalRoutesFor( - String ifname, HashSet<IpPrefix> prefixes) { - final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>(); - for (IpPrefix ipp : prefixes) { - localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST)); - } - return localRoutes; - } - - // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1. - private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) { - final byte[] dnsBytes = localPrefix.getRawAddress(); - dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1)); - try { - return Inet6Address.getByAddress(null, dnsBytes, 0); - } catch (UnknownHostException e) { - Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix); - return null; - } - } - - private static byte getRandomSanitizedByte(byte dflt, byte... excluded) { - final byte random = (byte) (new Random()).nextInt(); - for (int value : excluded) { - if (random == value) return dflt; - } - return random; - } -} diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java deleted file mode 100644 index 73fc833fabf5..000000000000 --- a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.AF_PACKET; -import static android.system.OsConstants.ETH_P_IPV6; -import static android.system.OsConstants.IPPROTO_RAW; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_NONBLOCK; -import static android.system.OsConstants.SOCK_RAW; - -import android.net.util.InterfaceParams; -import android.net.util.PacketReader; -import android.net.util.SocketUtils; -import android.net.util.TetheringUtils; -import android.os.Handler; -import android.system.ErrnoException; -import android.system.Os; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Arrays; - -/** - * Basic IPv6 Neighbor Advertisement Forwarder. - * - * Forward NA packets from upstream iface to tethered iface - * and NS packets from tethered iface to upstream iface. - * - * @hide - */ -public class NeighborPacketForwarder extends PacketReader { - private final String mTag; - - private FileDescriptor mFd; - - // TODO: get these from NetworkStackConstants. - private static final int IPV6_ADDR_LEN = 16; - private static final int IPV6_DST_ADDR_OFFSET = 24; - private static final int IPV6_HEADER_LEN = 40; - private static final int ETH_HEADER_LEN = 14; - - private InterfaceParams mListenIfaceParams, mSendIfaceParams; - - private final int mType; - public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136; - public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; - - public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) { - super(h); - mTag = NeighborPacketForwarder.class.getSimpleName() + "-" - + tetheredInterface.name + "-" + type; - mType = type; - - if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { - mSendIfaceParams = tetheredInterface; - } else { - mListenIfaceParams = tetheredInterface; - } - } - - /** Set new upstream iface and start/stop based on new params. */ - public void setUpstreamIface(InterfaceParams upstreamParams) { - final InterfaceParams oldUpstreamParams; - - if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { - oldUpstreamParams = mListenIfaceParams; - mListenIfaceParams = upstreamParams; - } else { - oldUpstreamParams = mSendIfaceParams; - mSendIfaceParams = upstreamParams; - } - - if (oldUpstreamParams == null && upstreamParams != null) { - start(); - } else if (oldUpstreamParams != null && upstreamParams == null) { - stop(); - } else if (oldUpstreamParams != null && upstreamParams != null - && oldUpstreamParams.index != upstreamParams.index) { - stop(); - start(); - } - } - - // TODO: move NetworkStackUtils.closeSocketQuietly to - // frameworks/libs/net/common/device/com/android/net/module/util/[someclass]. - private void closeSocketQuietly(FileDescriptor fd) { - try { - SocketUtils.closeSocket(fd); - } catch (IOException ignored) { - } - } - - @Override - protected FileDescriptor createFd() { - try { - // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used. - // To keep uniformity in both directions PACKET socket can be used. - mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); - - // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type? - if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { - TetheringUtils.setupNaSocket(mFd); - } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) { - TetheringUtils.setupNsSocket(mFd); - } - - SocketAddress bindAddress = SocketUtils.makePacketSocketAddress( - ETH_P_IPV6, mListenIfaceParams.index); - Os.bind(mFd, bindAddress); - } catch (ErrnoException | SocketException e) { - Log.wtf(mTag, "Failed to create socket", e); - closeSocketQuietly(mFd); - return null; - } - - return mFd; - } - - private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) { - Inet6Address dstAddr; - try { - dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf, - IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN)); - } catch (UnknownHostException | ClassCastException impossible) { - throw new AssertionError("16-byte array not valid IPv6 address?"); - } - return dstAddr; - } - - @Override - protected void handlePacket(byte[] recvbuf, int length) { - if (mSendIfaceParams == null) { - return; - } - - // The BPF filter should already have checked the length of the packet, but... - if (length < IPV6_HEADER_LEN) { - return; - } - Inet6Address destv6 = getIpv6DestinationAddress(recvbuf); - if (!destv6.isMulticastAddress()) { - return; - } - InetSocketAddress dest = new InetSocketAddress(destv6, 0); - - FileDescriptor fd = null; - try { - fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); - SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name); - - int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest); - } catch (ErrnoException | SocketException e) { - Log.e(mTag, "handlePacket error: " + e); - } finally { - closeSocketQuietly(fd); - } - } -} diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java deleted file mode 100644 index 7c0b7cc7515c..000000000000 --- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.net.util.NetworkConstants.IPV6_MIN_MTU; -import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; -import static android.net.util.TetheringUtils.getAllNodesForScopeId; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.SOCK_RAW; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_SNDTIMEO; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.TrafficStats; -import android.net.util.InterfaceParams; -import android.net.util.SocketUtils; -import android.net.util.TetheringUtils; -import android.system.ErrnoException; -import android.system.Os; -import android.system.StructTimeval; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.TrafficStatsConstants; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - - -/** - * Basic IPv6 Router Advertisement Daemon. - * - * TODO: - * - * - Rewrite using Handler (and friends) so that AlarmManager can deliver - * "kick" messages when it's time to send a multicast RA. - * - * @hide - */ -public class RouterAdvertisementDaemon { - private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName(); - private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133); - private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134); - private static final int MIN_RA_HEADER_SIZE = 16; - - // Summary of various timers and lifetimes. - private static final int MIN_RTR_ADV_INTERVAL_SEC = 300; - private static final int MAX_RTR_ADV_INTERVAL_SEC = 600; - // In general, router, prefix, and DNS lifetimes are all advised to be - // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double - // that to allow for multicast packet loss. - // - // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent - // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of - // "approximately 7 RAs per hour". - private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC; - // From https://tools.ietf.org/html/rfc4861#section-10 . - private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3; - // Both initial and final RAs, but also for changes in RA contents. - // From https://tools.ietf.org/html/rfc4861#section-10 . - private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5; - - private static final int DAY_IN_SECONDS = 86_400; - - private final InterfaceParams mInterface; - private final InetSocketAddress mAllNodes; - - // This lock is to protect the RA from being updated while being - // transmitted on another thread (multicast or unicast). - // - // TODO: This should be handled with a more RCU-like approach. - private final Object mLock = new Object(); - @GuardedBy("mLock") - private final byte[] mRA = new byte[IPV6_MIN_MTU]; - @GuardedBy("mLock") - private int mRaLength; - @GuardedBy("mLock") - private final DeprecatedInfoTracker mDeprecatedInfoTracker; - @GuardedBy("mLock") - private RaParams mRaParams; - - private volatile FileDescriptor mSocket; - private volatile MulticastTransmitter mMulticastTransmitter; - private volatile UnicastResponder mUnicastResponder; - - /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/ - public static class RaParams { - // Tethered traffic will have the hop limit properly decremented. - // Consequently, set the hoplimit greater by one than the upstream - // unicast hop limit. - // - // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the - // upstream interface for more correct behaviour. - static final byte DEFAULT_HOPLIMIT = 65; - - public boolean hasDefaultRoute; - public byte hopLimit; - public int mtu; - public HashSet<IpPrefix> prefixes; - public HashSet<Inet6Address> dnses; - - public RaParams() { - hasDefaultRoute = false; - hopLimit = DEFAULT_HOPLIMIT; - mtu = IPV6_MIN_MTU; - prefixes = new HashSet<IpPrefix>(); - dnses = new HashSet<Inet6Address>(); - } - - public RaParams(RaParams other) { - hasDefaultRoute = other.hasDefaultRoute; - hopLimit = other.hopLimit; - mtu = other.mtu; - prefixes = (HashSet) other.prefixes.clone(); - dnses = (HashSet) other.dnses.clone(); - } - - /** - * Returns the subset of RA parameters that become deprecated when - * moving from announcing oldRa to announcing newRa. - * - * Currently only tracks differences in |prefixes| and |dnses|. - */ - public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) { - RaParams newlyDeprecated = new RaParams(); - - if (oldRa != null) { - for (IpPrefix ipp : oldRa.prefixes) { - if (newRa == null || !newRa.prefixes.contains(ipp)) { - newlyDeprecated.prefixes.add(ipp); - } - } - - for (Inet6Address dns : oldRa.dnses) { - if (newRa == null || !newRa.dnses.contains(dns)) { - newlyDeprecated.dnses.add(dns); - } - } - } - - return newlyDeprecated; - } - } - - private static class DeprecatedInfoTracker { - private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>(); - private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>(); - - Set<IpPrefix> getPrefixes() { - return mPrefixes.keySet(); - } - - void putPrefixes(Set<IpPrefix> prefixes) { - for (IpPrefix ipp : prefixes) { - mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS); - } - } - - void removePrefixes(Set<IpPrefix> prefixes) { - for (IpPrefix ipp : prefixes) { - mPrefixes.remove(ipp); - } - } - - Set<Inet6Address> getDnses() { - return mDnses.keySet(); - } - - void putDnses(Set<Inet6Address> dnses) { - for (Inet6Address dns : dnses) { - mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS); - } - } - - void removeDnses(Set<Inet6Address> dnses) { - for (Inet6Address dns : dnses) { - mDnses.remove(dns); - } - } - - boolean isEmpty() { - return mPrefixes.isEmpty() && mDnses.isEmpty(); - } - - private boolean decrementCounters() { - boolean removed = decrementCounter(mPrefixes); - removed |= decrementCounter(mDnses); - return removed; - } - - private <T> boolean decrementCounter(HashMap<T, Integer> map) { - boolean removed = false; - - for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator(); - it.hasNext();) { - Map.Entry<T, Integer> kv = it.next(); - if (kv.getValue() == 0) { - it.remove(); - removed = true; - } else { - kv.setValue(kv.getValue() - 1); - } - } - - return removed; - } - } - - public RouterAdvertisementDaemon(InterfaceParams ifParams) { - mInterface = ifParams; - mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0); - mDeprecatedInfoTracker = new DeprecatedInfoTracker(); - } - - /** Build new RA.*/ - public void buildNewRa(RaParams deprecatedParams, RaParams newParams) { - synchronized (mLock) { - if (deprecatedParams != null) { - mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes); - mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses); - } - - if (newParams != null) { - // Process information that is no longer deprecated. - mDeprecatedInfoTracker.removePrefixes(newParams.prefixes); - mDeprecatedInfoTracker.removeDnses(newParams.dnses); - } - - mRaParams = newParams; - assembleRaLocked(); - } - - maybeNotifyMulticastTransmitter(); - } - - /** Start router advertisement daemon. */ - public boolean start() { - if (!createSocket()) { - return false; - } - - mMulticastTransmitter = new MulticastTransmitter(); - mMulticastTransmitter.start(); - - mUnicastResponder = new UnicastResponder(); - mUnicastResponder.start(); - - return true; - } - - /** Stop router advertisement daemon. */ - public void stop() { - closeSocket(); - // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before - // the thread's termination. - maybeNotifyMulticastTransmitter(); - mMulticastTransmitter = null; - mUnicastResponder = null; - } - - @GuardedBy("mLock") - private void assembleRaLocked() { - final ByteBuffer ra = ByteBuffer.wrap(mRA); - ra.order(ByteOrder.BIG_ENDIAN); - - final boolean haveRaParams = (mRaParams != null); - boolean shouldSendRA = false; - - try { - putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute, - haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT); - putSlla(ra, mInterface.macAddr.toByteArray()); - mRaLength = ra.position(); - - // https://tools.ietf.org/html/rfc5175#section-4 says: - // - // "MUST NOT be added to a Router Advertisement message - // if no flags in the option are set." - // - // putExpandedFlagsOption(ra); - - if (haveRaParams) { - putMtu(ra, mRaParams.mtu); - mRaLength = ra.position(); - - for (IpPrefix ipp : mRaParams.prefixes) { - putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME); - mRaLength = ra.position(); - shouldSendRA = true; - } - - if (mRaParams.dnses.size() > 0) { - putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME); - mRaLength = ra.position(); - shouldSendRA = true; - } - } - - for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) { - putPio(ra, ipp, 0, 0); - mRaLength = ra.position(); - shouldSendRA = true; - } - - final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses(); - if (!deprecatedDnses.isEmpty()) { - putRdnss(ra, deprecatedDnses, 0); - mRaLength = ra.position(); - shouldSendRA = true; - } - } catch (BufferOverflowException e) { - // The packet up to mRaLength is valid, since it has been updated - // progressively as the RA was built. Log an error, and continue - // on as best as possible. - Log.e(TAG, "Could not construct new RA: " + e); - } - - // We have nothing worth announcing; indicate as much to maybeSendRA(). - if (!shouldSendRA) { - mRaLength = 0; - } - } - - private void maybeNotifyMulticastTransmitter() { - final MulticastTransmitter m = mMulticastTransmitter; - if (m != null) { - m.hup(); - } - } - - private static byte asByte(int value) { - return (byte) value; - } - private static short asShort(int value) { - return (short) value; - } - - private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) { - /** - Router Advertisement Message Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reachable Time | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Retrans Timer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Options ... - +-+-+-+-+-+-+-+-+-+-+-+- - */ - ra.put(ICMPV6_ND_ROUTER_ADVERT) - .put(asByte(0)) - .putShort(asShort(0)) - .put(hopLimit) - // RFC 4191 "high" preference, iff. advertising a default route. - .put(hasDefaultRoute ? asByte(0x08) : asByte(0)) - .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0)) - .putInt(0) - .putInt(0); - } - - private static void putSlla(ByteBuffer ra, byte[] slla) { - /** - Source/Target Link-layer Address - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Link-Layer Address ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - if (slla == null || slla.length != 6) { - // Only IEEE 802.3 6-byte addresses are supported. - return; - } - - final byte nd_option_slla = 1; - final byte slla_num_8octets = 1; - ra.put(nd_option_slla) - .put(slla_num_8octets) - .put(slla); - } - - private static void putExpandedFlagsOption(ByteBuffer ra) { - /** - Router Advertisement Expanded Flags Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Bit fields available .. - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ... for assignment | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - final byte nd_option__efo = 26; - final byte efo_num_8octets = 1; - - ra.put(nd_option__efo) - .put(efo_num_8octets) - .putShort(asShort(0)) - .putInt(0); - } - - private static void putMtu(ByteBuffer ra, int mtu) { - /** - MTU - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | MTU | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final byte nd_option_mtu = 5; - final byte mtu_num_8octs = 1; - ra.put(nd_option_mtu) - .put(mtu_num_8octs) - .putShort(asShort(0)) - .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu); - } - - private static void putPio(ByteBuffer ra, IpPrefix ipp, - int validTime, int preferredTime) { - /** - Prefix Information - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Prefix Length |L|A| Reserved1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Valid Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Preferred Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reserved2 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + + - | | - + Prefix + - | | - + + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final int prefixLength = ipp.getPrefixLength(); - if (prefixLength != 64) { - return; - } - final byte nd_option_pio = 3; - final byte pio_num_8octets = 4; - - if (validTime < 0) validTime = 0; - if (preferredTime < 0) preferredTime = 0; - if (preferredTime > validTime) preferredTime = validTime; - - final byte[] addr = ipp.getAddress().getAddress(); - ra.put(nd_option_pio) - .put(pio_num_8octets) - .put(asByte(prefixLength)) - .put(asByte(0xc0)) /* L & A set */ - .putInt(validTime) - .putInt(preferredTime) - .putInt(0) - .put(addr); - } - - private static void putRio(ByteBuffer ra, IpPrefix ipp) { - /** - Route Information Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Prefix Length |Resvd|Prf|Resvd| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Route Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Prefix (Variable Length) | - . . - . . - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final int prefixLength = ipp.getPrefixLength(); - if (prefixLength > 64) { - return; - } - final byte nd_option_rio = 24; - final byte rio_num_8octets = asByte( - (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3); - - final byte[] addr = ipp.getAddress().getAddress(); - ra.put(nd_option_rio) - .put(rio_num_8octets) - .put(asByte(prefixLength)) - .put(asByte(0x18)) - .putInt(DEFAULT_LIFETIME); - - // Rely upon an IpPrefix's address being properly zeroed. - if (prefixLength > 0) { - ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16); - } - } - - private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) { - /** - Recursive DNS Server (RDNSS) Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - : Addresses of IPv6 Recursive DNS Servers : - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - final HashSet<Inet6Address> filteredDnses = new HashSet<>(); - for (Inet6Address dns : dnses) { - if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) { - filteredDnses.add(dns); - } - } - if (filteredDnses.isEmpty()) return; - - final byte nd_option_rdnss = 25; - final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1); - ra.put(nd_option_rdnss) - .put(rdnss_num_8octets) - .putShort(asShort(0)) - .putInt(lifetime); - - for (Inet6Address dns : filteredDnses) { - // NOTE: If the full of list DNS servers doesn't fit in the packet, - // this code will cause a buffer overflow and the RA won't include - // this instance of the option at all. - // - // TODO: Consider looking at ra.remaining() to determine how many - // DNS servers will fit, and adding only those. - ra.put(dns.getAddress()); - } - } - - private boolean createSocket() { - final int send_timout_ms = 300; - - final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR); - try { - mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - // Setting SNDTIMEO is purely for defensive purposes. - Os.setsockoptTimeval( - mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms)); - SocketUtils.bindSocketToInterface(mSocket, mInterface.name); - TetheringUtils.setupRaSocket(mSocket, mInterface.index); - } catch (ErrnoException | IOException e) { - Log.e(TAG, "Failed to create RA daemon socket: " + e); - return false; - } finally { - TrafficStats.setThreadStatsTag(oldTag); - } - - return true; - } - - private void closeSocket() { - if (mSocket != null) { - try { - SocketUtils.closeSocket(mSocket); - } catch (IOException ignored) { } - } - mSocket = null; - } - - private boolean isSocketValid() { - final FileDescriptor s = mSocket; - return (s != null) && s.valid(); - } - - private boolean isSuitableDestination(InetSocketAddress dest) { - if (mAllNodes.equals(dest)) { - return true; - } - - final InetAddress destip = dest.getAddress(); - return (destip instanceof Inet6Address) - && destip.isLinkLocalAddress() - && (((Inet6Address) destip).getScopeId() == mInterface.index); - } - - private void maybeSendRA(InetSocketAddress dest) { - if (dest == null || !isSuitableDestination(dest)) { - dest = mAllNodes; - } - - try { - synchronized (mLock) { - if (mRaLength < MIN_RA_HEADER_SIZE) { - // No actual RA to send. - return; - } - Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest); - } - Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress()); - } catch (ErrnoException | SocketException e) { - if (isSocketValid()) { - Log.e(TAG, "sendto error: " + e); - } - } - } - - private final class UnicastResponder extends Thread { - private final InetSocketAddress mSolicitor = new InetSocketAddress(0); - // The recycled buffer for receiving Router Solicitations from clients. - // If the RS is larger than IPV6_MIN_MTU the packets are truncated. - // This is fine since currently only byte 0 is examined anyway. - private final byte[] mSolicitation = new byte[IPV6_MIN_MTU]; - - @Override - public void run() { - while (isSocketValid()) { - try { - // Blocking receive. - final int rval = Os.recvfrom( - mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor); - // Do the least possible amount of validation. - if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) { - continue; - } - } catch (ErrnoException | SocketException e) { - if (isSocketValid()) { - Log.e(TAG, "recvfrom error: " + e); - } - continue; - } - - maybeSendRA(mSolicitor); - } - } - } - - // TODO: Consider moving this to run on a provided Looper as a Handler, - // with WakeupMessage-style messages providing the timer driven input. - private final class MulticastTransmitter extends Thread { - private final Random mRandom = new Random(); - private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0); - - @Override - public void run() { - while (isSocketValid()) { - try { - Thread.sleep(getNextMulticastTransmitDelayMs()); - } catch (InterruptedException ignored) { - // Stop sleeping, immediately send an RA, and continue. - } - - maybeSendRA(mAllNodes); - synchronized (mLock) { - if (mDeprecatedInfoTracker.decrementCounters()) { - // At least one deprecated PIO has been removed; - // reassemble the RA. - assembleRaLocked(); - } - } - } - } - - public void hup() { - // Set to one fewer that the desired number, because as soon as - // the thread interrupt is processed we immediately send an RA - // and mUrgentAnnouncements is not examined until the subsequent - // sleep interval computation (i.e. this way we send 3 and not 4). - mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1); - interrupt(); - } - - private int getNextMulticastTransmitDelaySec() { - boolean deprecationInProgress = false; - synchronized (mLock) { - if (mRaLength < MIN_RA_HEADER_SIZE) { - // No actual RA to send; just sleep for 1 day. - return DAY_IN_SECONDS; - } - deprecationInProgress = !mDeprecatedInfoTracker.isEmpty(); - } - - final int urgentPending = mUrgentAnnouncements.getAndDecrement(); - if ((urgentPending > 0) || deprecationInProgress) { - return MIN_DELAY_BETWEEN_RAS_SEC; - } - - return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt( - MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC); - } - - private long getNextMulticastTransmitDelayMs() { - return 1000 * (long) getNextMulticastTransmitDelaySec(); - } - } -} diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java deleted file mode 100644 index b1ffdb01f5f3..000000000000 --- a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -import android.net.INetdUnsolicitedEventListener; - -import androidx.annotation.NonNull; - -/** - * Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be - * overridden. - */ -public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub { - - @Override - public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, - int uid) { } - - @Override - public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { } - - @Override - public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS, - @NonNull String[] servers) { } - - @Override - public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags, - int scope) { } - - @Override - public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags, - int scope) { } - - @Override - public void onInterfaceAdded(@NonNull String ifName) { } - - @Override - public void onInterfaceRemoved(@NonNull String ifName) { } - - @Override - public void onInterfaceChanged(@NonNull String ifName, boolean up) { } - - @Override - public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { } - - @Override - public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway, - @NonNull String ifName) { } - - @Override - public void onStrictCleartextDetected(int uid, @NonNull String hex) { } - - @Override - public int getInterfaceVersion() { - return INetdUnsolicitedEventListener.VERSION; - } - - @Override - public String getInterfaceHash() { - return INetdUnsolicitedEventListener.HASH; - } -} diff --git a/packages/Tethering/src/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java deleted file mode 100644 index 758978711bd4..000000000000 --- a/packages/Tethering/src/android/net/util/InterfaceSet.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.StringJoiner; - - -/** - * @hide - */ -public class InterfaceSet { - public final Set<String> ifnames; - - public InterfaceSet(String... names) { - final Set<String> nameSet = new HashSet<>(); - for (String name : names) { - if (name != null) nameSet.add(name); - } - ifnames = Collections.unmodifiableSet(nameSet); - } - - @Override - public String toString() { - final StringJoiner sj = new StringJoiner(",", "[", "]"); - for (String ifname : ifnames) sj.add(ifname); - return sj.toString(); - } - - @Override - public boolean equals(Object obj) { - return obj != null - && obj instanceof InterfaceSet - && ifnames.equals(((InterfaceSet) obj).ifnames); - } -} diff --git a/packages/Tethering/src/android/net/util/PrefixUtils.java b/packages/Tethering/src/android/net/util/PrefixUtils.java deleted file mode 100644 index f203e9995f3d..000000000000 --- a/packages/Tethering/src/android/net/util/PrefixUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - - -/** - * @hide - */ -public class PrefixUtils { - private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = { - pfx("127.0.0.0/8"), // IPv4 loopback - pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8 - pfx("::/3"), - pfx("fe80::/64"), // IPv6 link-local - pfx("fc00::/7"), // IPv6 ULA - pfx("ff02::/8"), // IPv6 link-local multicast - }; - - public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24"); - - /** Get non forwardable prefixes. */ - public static Set<IpPrefix> getNonForwardablePrefixes() { - final HashSet<IpPrefix> prefixes = new HashSet<>(); - addNonForwardablePrefixes(prefixes); - return prefixes; - } - - /** Add non forwardable prefixes. */ - public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) { - Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES); - } - - /** Get local prefixes from |lp|. */ - public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) { - final HashSet<IpPrefix> localPrefixes = new HashSet<>(); - if (lp == null) return localPrefixes; - - for (LinkAddress addr : lp.getAllLinkAddresses()) { - if (addr.getAddress().isLinkLocalAddress()) continue; - localPrefixes.add(asIpPrefix(addr)); - } - // TODO: Add directly-connected routes as well (ones from which we did - // not also form a LinkAddress)? - - return localPrefixes; - } - - /** Convert LinkAddress |addr| to IpPrefix. */ - public static IpPrefix asIpPrefix(LinkAddress addr) { - return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); - } - - /** Convert InetAddress |ip| to IpPrefix. */ - public static IpPrefix ipAddressAsPrefix(InetAddress ip) { - final int bitLength = (ip instanceof Inet4Address) - ? NetworkConstants.IPV4_ADDR_BITS - : NetworkConstants.IPV6_ADDR_BITS; - return new IpPrefix(ip, bitLength); - } - - private static IpPrefix pfx(String prefixStr) { - return new IpPrefix(prefixStr); - } -} diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java deleted file mode 100644 index 53b54f7de05d..000000000000 --- a/packages/Tethering/src/android/net/util/TetheringUtils.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -import android.net.TetherStatsParcel; -import android.net.TetheringRequestParcel; -import android.util.Log; - -import androidx.annotation.NonNull; - -import java.io.FileDescriptor; -import java.net.Inet6Address; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Objects; - -/** - * The classes and the methods for tethering utilization. - * - * {@hide} - */ -public class TetheringUtils { - public static final byte[] ALL_NODES = new byte[] { - (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - }; - - /** - * Configures a socket for receiving and sending ICMPv6 neighbor advertisments. - * @param fd the socket's {@link FileDescriptor}. - */ - public static native void setupNaSocket(FileDescriptor fd) - throws SocketException; - - /** - * Configures a socket for receiving and sending ICMPv6 neighbor solicitations. - * @param fd the socket's {@link FileDescriptor}. - */ - public static native void setupNsSocket(FileDescriptor fd) - throws SocketException; - - /** - * The object which records offload Tx/Rx forwarded bytes/packets. - * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with - * this class as well. - */ - public static class ForwardedStats { - public final long rxBytes; - public final long rxPackets; - public final long txBytes; - public final long txPackets; - - public ForwardedStats() { - rxBytes = 0; - rxPackets = 0; - txBytes = 0; - txPackets = 0; - } - - public ForwardedStats(long rxBytes, long txBytes) { - this.rxBytes = rxBytes; - this.rxPackets = 0; - this.txBytes = txBytes; - this.txPackets = 0; - } - - public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) { - this.rxBytes = rxBytes; - this.rxPackets = rxPackets; - this.txBytes = txBytes; - this.txPackets = txPackets; - } - - public ForwardedStats(@NonNull TetherStatsParcel tetherStats) { - rxBytes = tetherStats.rxBytes; - rxPackets = tetherStats.rxPackets; - txBytes = tetherStats.txBytes; - txPackets = tetherStats.txPackets; - } - - public ForwardedStats(@NonNull ForwardedStats other) { - rxBytes = other.rxBytes; - rxPackets = other.rxPackets; - txBytes = other.txBytes; - txPackets = other.txPackets; - } - - /** Add Tx/Rx bytes/packets and return the result as a new object. */ - @NonNull - public ForwardedStats add(@NonNull ForwardedStats other) { - return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets, - txBytes + other.txBytes, txPackets + other.txPackets); - } - - /** Subtract Tx/Rx bytes/packets and return the result as a new object. */ - @NonNull - public ForwardedStats subtract(@NonNull ForwardedStats other) { - // TODO: Perhaps throw an exception if any negative difference value just in case. - final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0); - final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0); - final long txBytesDiff = Math.max(txBytes - other.txBytes, 0); - final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0); - return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff); - } - - /** Returns the string representation of this object. */ - @NonNull - public String toString() { - return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes, - rxPackets, txBytes, txPackets); - } - } - - /** - * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. - * @param fd the socket's {@link FileDescriptor}. - * @param ifIndex the interface index. - */ - public static native void setupRaSocket(FileDescriptor fd, int ifIndex) - throws SocketException; - - /** - * Read s as an unsigned 16-bit integer. - */ - public static int uint16(short s) { - return s & 0xffff; - } - - /** Check whether two TetheringRequestParcels are the same. */ - public static boolean isTetheringRequestEquals(final TetheringRequestParcel request, - final TetheringRequestParcel otherRequest) { - if (request == otherRequest) return true; - - return request != null && otherRequest != null - && request.tetheringType == otherRequest.tetheringType - && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address) - && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress) - && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck - && request.showProvisioningUi == otherRequest.showProvisioningUi; - } - - /** Get inet6 address for all nodes given scope ID. */ - public static Inet6Address getAllNodesForScopeId(int scopeId) { - try { - return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); - } catch (UnknownHostException uhe) { - Log.wtf("TetheringUtils", "Failed to construct Inet6Address from " - + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId); - return null; - } - } -} diff --git a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java b/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java deleted file mode 100644 index e2804abd752b..000000000000 --- a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.util.Log; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - - -/** - * A utility class that runs the provided callback on the provided handler when - * intents matching the provided filter arrive. Intents received by a stale - * receiver are safely ignored. - * - * Calls to startListening() and stopListening() must happen on the same thread. - * - * @hide - */ -public class VersionedBroadcastListener { - private static final boolean DBG = false; - - private final String mTag; - private final Context mContext; - private final Handler mHandler; - private final IntentFilter mFilter; - private final Consumer<Intent> mCallback; - private final AtomicInteger mGenerationNumber; - private BroadcastReceiver mReceiver; - - public VersionedBroadcastListener(String tag, Context ctx, Handler handler, - IntentFilter filter, Consumer<Intent> callback) { - mTag = tag; - mContext = ctx; - mHandler = handler; - mFilter = filter; - mCallback = callback; - mGenerationNumber = new AtomicInteger(0); - } - - /** Start listening to intent broadcast. */ - public void startListening() { - if (DBG) Log.d(mTag, "startListening"); - if (mReceiver != null) return; - - mReceiver = new Receiver(mTag, mGenerationNumber, mCallback); - mContext.registerReceiver(mReceiver, mFilter, null, mHandler); - } - - /** Stop listening to intent broadcast. */ - public void stopListening() { - if (DBG) Log.d(mTag, "stopListening"); - if (mReceiver == null) return; - - mGenerationNumber.incrementAndGet(); - mContext.unregisterReceiver(mReceiver); - mReceiver = null; - } - - private static class Receiver extends BroadcastReceiver { - public final String tag; - public final AtomicInteger atomicGenerationNumber; - public final Consumer<Intent> callback; - // Used to verify this receiver is still current. - public final int generationNumber; - - Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) { - this.tag = tag; - this.atomicGenerationNumber = atomicGenerationNumber; - this.callback = callback; - generationNumber = atomicGenerationNumber.incrementAndGet(); - } - - @Override - public void onReceive(Context context, Intent intent) { - final int currentGenerationNumber = atomicGenerationNumber.get(); - - if (DBG) { - Log.d(tag, "receiver generationNumber=" + generationNumber - + ", current generationNumber=" + currentGenerationNumber); - } - if (generationNumber != currentGenerationNumber) return; - - callback.accept(intent); - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java deleted file mode 100644 index 20f30ea7a460..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ /dev/null @@ -1,778 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; - -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - -import android.app.usage.NetworkStatsManager; -import android.net.INetd; -import android.net.MacAddress; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; -import android.net.TetherOffloadRuleParcel; -import android.net.TetherStatsParcel; -import android.net.ip.IpServer; -import android.net.netstats.provider.NetworkStatsProvider; -import android.net.util.SharedLog; -import android.net.util.TetheringUtils.ForwardedStats; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.text.TextUtils; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; - -import java.net.Inet6Address; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This coordinator is responsible for providing BPF offload relevant functionality. - * - Get tethering stats. - * - Set data limit. - * - Set global alert. - * - Add/remove forwarding rules. - * - * @hide - */ -public class BpfCoordinator { - private static final String TAG = BpfCoordinator.class.getSimpleName(); - private static final int DUMP_TIMEOUT_MS = 10_000; - - @VisibleForTesting - enum StatsType { - STATS_PER_IFACE, - STATS_PER_UID, - } - - @NonNull - private final Handler mHandler; - @NonNull - private final INetd mNetd; - @NonNull - private final SharedLog mLog; - @NonNull - private final Dependencies mDeps; - @Nullable - private final BpfTetherStatsProvider mStatsProvider; - - // True if BPF offload is supported, false otherwise. The BPF offload could be disabled by - // a runtime resource overlay package or device configuration. This flag is only initialized - // in the constructor because it is hard to unwind all existing change once device - // configuration is changed. Especially the forwarding rules. Keep the same setting - // to make it simpler. See also TetheringConfiguration. - private final boolean mIsBpfEnabled; - - // Tracks whether BPF tethering is started or not. This is set by tethering before it - // starts the first IpServer and is cleared by tethering shortly before the last IpServer - // is stopped. Note that rule updates (especially deletions, but sometimes additions as - // well) may arrive when this is false. If they do, they must be communicated to netd. - // Changes in data limits may also arrive when this is false, and if they do, they must - // also be communicated to netd. - private boolean mPollingStarted = false; - - // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert - // quota is interface independent and global for tether offload. - private long mRemainingAlertQuota = QUOTA_UNLIMITED; - - // Maps upstream interface index to offloaded traffic statistics. - // Always contains the latest total bytes/packets, since each upstream was started, received - // from the BPF maps for each interface. - private final SparseArray<ForwardedStats> mStats = new SparseArray<>(); - - // Maps upstream interface names to interface quotas. - // Always contains the latest value received from the framework for each interface, regardless - // of whether offload is currently running (or is even supported) on that interface. Only - // includes interfaces that have a quota set. Note that this map is used for storing the quota - // which is set from the service. Because the service uses the interface name to present the - // interface, this map uses the interface name to be the mapping index. - private final HashMap<String, Long> mInterfaceQuotas = new HashMap<>(); - - // Maps upstream interface index to interface names. - // Store all interface name since boot. Used for lookup what interface name it is from the - // tether stats got from netd because netd reports interface index to present an interface. - // TODO: Remove the unused interface name. - private final SparseArray<String> mInterfaceNames = new SparseArray<>(); - - // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a - // given downstream. Each map: - // - Is owned by the IpServer that is responsible for that downstream. - // - Must only be modified by that IpServer. - // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes - // its last rule (or clears its rules). - // TODO: Perhaps seal the map and rule operations which communicates with netd into a class. - // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be - // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear - // and tetherOffloadRuleUpdate? - // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream - // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream - // interface index while calling function to clear all rules. IpServer may be calling clear - // rules function without a valid IPv6 downstream interface index even if it may have one - // before. IpServer would need to call getInterfaceParams() in the constructor instead of when - // startIpv6() is called, and make mInterfaceParams final. - private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> - mIpv6ForwardingRules = new LinkedHashMap<>(); - - // Runnable that used by scheduling next polling of stats. - private final Runnable mScheduledPollingTask = () -> { - updateForwardedStatsFromNetd(); - maybeSchedulePollingStats(); - }; - - @VisibleForTesting - public abstract static class Dependencies { - /** Get handler. */ - @NonNull public abstract Handler getHandler(); - - /** Get netd. */ - @NonNull public abstract INetd getNetd(); - - /** Get network stats manager. */ - @NonNull public abstract NetworkStatsManager getNetworkStatsManager(); - - /** Get shared log. */ - @NonNull public abstract SharedLog getSharedLog(); - - /** Get tethering configuration. */ - @Nullable public abstract TetheringConfiguration getTetherConfig(); - } - - @VisibleForTesting - public BpfCoordinator(@NonNull Dependencies deps) { - mDeps = deps; - mHandler = mDeps.getHandler(); - mNetd = mDeps.getNetd(); - mLog = mDeps.getSharedLog().forSubComponent(TAG); - mIsBpfEnabled = isBpfEnabled(); - BpfTetherStatsProvider provider = new BpfTetherStatsProvider(); - try { - mDeps.getNetworkStatsManager().registerNetworkStatsProvider( - getClass().getSimpleName(), provider); - } catch (RuntimeException e) { - // TODO: Perhaps not allow to use BPF offload because the reregistration failure - // implied that no data limit could be applies on a metered upstream if any. - Log.wtf(TAG, "Cannot register offload stats provider: " + e); - provider = null; - } - mStatsProvider = provider; - } - - /** - * Start BPF tethering offload stats polling when the first upstream is started. - * Note that this can be only called on handler thread. - * TODO: Perhaps check BPF support before starting. - * TODO: Start the stats polling only if there is any client on the downstream. - */ - public void startPolling() { - if (mPollingStarted) return; - - if (!mIsBpfEnabled) { - mLog.i("Offload disabled"); - return; - } - - mPollingStarted = true; - maybeSchedulePollingStats(); - - mLog.i("Polling started"); - } - - /** - * Stop BPF tethering offload stats polling. - * The data limit cleanup and the tether stats maps cleanup are not implemented here. - * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the - * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup - * functionality. - * Note that this can be only called on handler thread. - */ - public void stopPolling() { - if (!mPollingStarted) return; - - // Stop scheduled polling tasks and poll the latest stats from BPF maps. - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - updateForwardedStatsFromNetd(); - mPollingStarted = false; - - mLog.i("Polling stopped"); - } - - /** - * Add forwarding rule. After adding the first rule on a given upstream, must add the data - * limit on the given upstream. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleAdd( - @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { - if (!mIsBpfEnabled) return; - - try { - // TODO: Perhaps avoid to add a duplicate rule. - mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Could not add IPv6 forwarding rule: ", e); - return; - } - - if (!mIpv6ForwardingRules.containsKey(ipServer)) { - mIpv6ForwardingRules.put(ipServer, new LinkedHashMap<Inet6Address, - Ipv6ForwardingRule>()); - } - LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer); - - // Setup the data limit on the given upstream if the first rule is added. - final int upstreamIfindex = rule.upstreamIfindex; - if (!isAnyRuleOnUpstream(upstreamIfindex)) { - // If failed to set a data limit, probably should not use this upstream, because - // the upstream may not want to blow through the data limit that was told to apply. - // TODO: Perhaps stop the coordinator. - boolean success = updateDataLimit(upstreamIfindex); - if (!success) { - final String iface = mInterfaceNames.get(upstreamIfindex); - mLog.e("Setting data limit for " + iface + " failed."); - } - } - - // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to - // check if it is about adding a first rule for a given upstream. - rules.put(rule.address, rule); - } - - /** - * Remove forwarding rule. After removing the last rule on a given upstream, must clear - * data limit, update the last tether stats and remove the tether stats in the BPF maps. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleRemove( - @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { - if (!mIsBpfEnabled) return; - - try { - // TODO: Perhaps avoid to remove a non-existent rule. - mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Could not remove IPv6 forwarding rule: ", e); - return; - } - - LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer); - if (rules == null) return; - - // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if - // the last rule is removed for a given upstream. If no rule is removed, return early. - // Avoid unnecessary work on a non-existent rule which may have never been added or - // removed already. - if (rules.remove(rule.address) == null) return; - - // Remove the downstream entry if it has no more rule. - if (rules.isEmpty()) { - mIpv6ForwardingRules.remove(ipServer); - } - - // Do cleanup functionality if there is no more rule on the given upstream. - final int upstreamIfindex = rule.upstreamIfindex; - if (!isAnyRuleOnUpstream(upstreamIfindex)) { - try { - final TetherStatsParcel stats = - mNetd.tetherOffloadGetAndClearStats(upstreamIfindex); - // Update the last stats delta and delete the local cache for a given upstream. - updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats}); - mStats.remove(upstreamIfindex); - } catch (RemoteException | ServiceSpecificException e) { - Log.wtf(TAG, "Exception when cleanup tether stats for upstream index " - + upstreamIfindex + ": ", e); - } - } - } - - /** - * Clear all forwarding rules for a given downstream. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) { - if (!mIsBpfEnabled) return; - - final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get( - ipServer); - if (rules == null) return; - - // Need to build a rule list because the rule map may be changed in the iteration. - for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) { - tetherOffloadRuleRemove(ipServer, rule); - } - } - - /** - * Update existing forwarding rules to new upstream for a given downstream. - * Note that this can be only called on handler thread. - */ - public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) { - if (!mIsBpfEnabled) return; - - final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get( - ipServer); - if (rules == null) return; - - // Need to build a rule list because the rule map may be changed in the iteration. - for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) { - // Remove the old rule before adding the new one because the map uses the same key for - // both rules. Reversing the processing order causes that the new rule is removed as - // unexpected. - // TODO: Add new rule first to reduce the latency which has no rule. - tetherOffloadRuleRemove(ipServer, rule); - tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex)); - } - } - - /** - * Add upstream name to lookup table. The lookup table is used for tether stats interface name - * lookup because the netd only reports interface index in BPF tether stats but the service - * expects the interface name in NetworkStats object. - * Note that this can be only called on handler thread. - */ - public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) { - if (!mIsBpfEnabled) return; - - if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return; - - // The same interface index to name mapping may be added by different IpServer objects or - // re-added by reconnection on the same upstream interface. Ignore the duplicate one. - final String iface = mInterfaceNames.get(upstreamIfindex); - if (iface == null) { - mInterfaceNames.put(upstreamIfindex, upstreamIface); - } else if (!TextUtils.equals(iface, upstreamIface)) { - Log.wtf(TAG, "The upstream interface name " + upstreamIface - + " is different from the existing interface name " - + iface + " for index " + upstreamIfindex); - } - } - - /** - * Dump information. - * Block the function until all the data are dumped on the handler thread or timed-out. The - * reason is that dumpsys invokes this function on the thread of caller and the data may only - * be allowed to be accessed on the handler thread. - */ - public void dump(@NonNull IndentingPrintWriter pw) { - final ConditionVariable dumpDone = new ConditionVariable(); - mHandler.post(() -> { - pw.println("mIsBpfEnabled: " + mIsBpfEnabled); - pw.println("Polling " + (mPollingStarted ? "started" : "not started")); - pw.println("Stats provider " + (mStatsProvider != null - ? "registered" : "not registered")); - pw.println("Upstream quota: " + mInterfaceQuotas.toString()); - pw.println("Polling interval: " + getPollingInterval() + " ms"); - - pw.println("Forwarding stats:"); - pw.increaseIndent(); - if (mStats.size() == 0) { - pw.println("<empty>"); - } else { - dumpStats(pw); - } - pw.decreaseIndent(); - - pw.println("Forwarding rules:"); - pw.increaseIndent(); - if (mIpv6ForwardingRules.size() == 0) { - pw.println("<empty>"); - } else { - dumpIpv6ForwardingRules(pw); - } - pw.decreaseIndent(); - - dumpDone.open(); - }); - if (!dumpDone.block(DUMP_TIMEOUT_MS)) { - pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms"); - } - } - - private void dumpStats(@NonNull IndentingPrintWriter pw) { - for (int i = 0; i < mStats.size(); i++) { - final int upstreamIfindex = mStats.keyAt(i); - final ForwardedStats stats = mStats.get(upstreamIfindex); - pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get( - upstreamIfindex), stats.toString())); - } - } - - private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) { - for (Map.Entry<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> entry : - mIpv6ForwardingRules.entrySet()) { - IpServer ipServer = entry.getKey(); - // The rule downstream interface index is paired with the interface name from - // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer. - final String downstreamIface = ipServer.interfaceName(); - pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac"); - - pw.increaseIndent(); - LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = entry.getValue(); - for (Ipv6ForwardingRule rule : rules.values()) { - final int upstreamIfindex = rule.upstreamIfindex; - pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex, - mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex, - downstreamIface, rule.address, rule.srcMac, rule.dstMac)); - } - pw.decreaseIndent(); - } - } - - /** IPv6 forwarding rule class. */ - public static class Ipv6ForwardingRule { - public final int upstreamIfindex; - public final int downstreamIfindex; - - @NonNull - public final Inet6Address address; - @NonNull - public final MacAddress srcMac; - @NonNull - public final MacAddress dstMac; - - public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, - @NonNull Inet6Address address, @NonNull MacAddress srcMac, - @NonNull MacAddress dstMac) { - this.upstreamIfindex = upstreamIfindex; - this.downstreamIfindex = downstreamIfIndex; - this.address = address; - this.srcMac = srcMac; - this.dstMac = dstMac; - } - - /** Return a new rule object which updates with new upstream index. */ - @NonNull - public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { - return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, - dstMac); - } - - /** - * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() - * would be error-prone due to generated stable AIDL classes not having a copy constructor. - */ - @NonNull - public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { - final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); - parcel.inputInterfaceIndex = upstreamIfindex; - parcel.outputInterfaceIndex = downstreamIfindex; - parcel.destination = address.getAddress(); - parcel.prefixLength = 128; - parcel.srcL2Address = srcMac.toByteArray(); - parcel.dstL2Address = dstMac.toByteArray(); - return parcel; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Ipv6ForwardingRule)) return false; - Ipv6ForwardingRule that = (Ipv6ForwardingRule) o; - return this.upstreamIfindex == that.upstreamIfindex - && this.downstreamIfindex == that.downstreamIfindex - && Objects.equals(this.address, that.address) - && Objects.equals(this.srcMac, that.srcMac) - && Objects.equals(this.dstMac, that.dstMac); - } - - @Override - public int hashCode() { - // TODO: if this is ever used in production code, don't pass ifindices - // to Objects.hash() to avoid autoboxing overhead. - return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac); - } - } - - /** - * A BPF tethering stats provider to provide network statistics to the system. - * Note that this class' data may only be accessed on the handler thread. - */ - @VisibleForTesting - class BpfTetherStatsProvider extends NetworkStatsProvider { - // The offloaded traffic statistics per interface that has not been reported since the - // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams - // and has pending tether stats delta are included in this NetworkStats object. - private NetworkStats mIfaceStats = new NetworkStats(0L, 0); - - // The same stats as above, but counts network stats per uid. - private NetworkStats mUidStats = new NetworkStats(0L, 0); - - @Override - public void onRequestStatsUpdate(int token) { - mHandler.post(() -> pushTetherStats()); - } - - @Override - public void onSetAlert(long quotaBytes) { - mHandler.post(() -> updateAlertQuota(quotaBytes)); - } - - @Override - public void onSetLimit(@NonNull String iface, long quotaBytes) { - if (quotaBytes < QUOTA_UNLIMITED) { - throw new IllegalArgumentException("invalid quota value " + quotaBytes); - } - - mHandler.post(() -> { - final Long curIfaceQuota = mInterfaceQuotas.get(iface); - - if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return; - - if (quotaBytes == QUOTA_UNLIMITED) { - mInterfaceQuotas.remove(iface); - } else { - mInterfaceQuotas.put(iface, quotaBytes); - } - maybeUpdateDataLimit(iface); - }); - } - - @VisibleForTesting - void pushTetherStats() { - try { - // The token is not used for now. See b/153606961. - notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats); - - // Clear the accumulated tether stats delta after reported. Note that create a new - // empty object because NetworkStats#clear is @hide. - mIfaceStats = new NetworkStats(0L, 0); - mUidStats = new NetworkStats(0L, 0); - } catch (RuntimeException e) { - mLog.e("Cannot report network stats: ", e); - } - } - - private void accumulateDiff(@NonNull NetworkStats ifaceDiff, - @NonNull NetworkStats uidDiff) { - mIfaceStats = mIfaceStats.add(ifaceDiff); - mUidStats = mUidStats.add(uidDiff); - } - } - - private boolean isBpfEnabled() { - final TetheringConfiguration config = mDeps.getTetherConfig(); - return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */; - } - - private int getInterfaceIndexFromRules(@NonNull String ifName) { - for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { - final int upstreamIfindex = rule.upstreamIfindex; - if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) { - return upstreamIfindex; - } - } - } - return 0; - } - - private long getQuotaBytes(@NonNull String iface) { - final Long limit = mInterfaceQuotas.get(iface); - final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED; - - return quotaBytes; - } - - private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) { - if (ifIndex == 0) { - Log.wtf(TAG, "Invalid interface index."); - return false; - } - - try { - mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Exception when updating quota " + quotaBytes + ": ", e); - return false; - } - - return true; - } - - // Handle the data limit update from the service which is the stats provider registered for. - private void maybeUpdateDataLimit(@NonNull String iface) { - // Set data limit only on a given upstream which has at least one rule. If we can't get - // an interface index for a given interface name, it means either there is no rule for - // a given upstream or the interface name is not an upstream which is monitored by the - // coordinator. - final int ifIndex = getInterfaceIndexFromRules(iface); - if (ifIndex == 0) return; - - final long quotaBytes = getQuotaBytes(iface); - sendDataLimitToNetd(ifIndex, quotaBytes); - } - - // Handle the data limit update while adding forwarding rules. - private boolean updateDataLimit(int ifIndex) { - final String iface = mInterfaceNames.get(ifIndex); - if (iface == null) { - mLog.e("Fail to get the interface name for index " + ifIndex); - return false; - } - final long quotaBytes = getQuotaBytes(iface); - return sendDataLimitToNetd(ifIndex, quotaBytes); - } - - private boolean isAnyRuleOnUpstream(int upstreamIfindex) { - for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { - if (upstreamIfindex == rule.upstreamIfindex) return true; - } - } - return false; - } - - @NonNull - private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex, - @NonNull final ForwardedStats diff) { - NetworkStats stats = new NetworkStats(0L, 0); - final String iface = mInterfaceNames.get(ifIndex); - if (iface == null) { - // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd. - // For now, netd may add the empty stats for the upstream which is not monitored by - // the coordinator. Silently ignore it. - return stats; - } - final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; - // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for - // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked. - return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets, - diff.txBytes, diff.txPackets, 0L /* operations */)); - } - - private void updateAlertQuota(long newQuota) { - if (newQuota < QUOTA_UNLIMITED) { - throw new IllegalArgumentException("invalid quota value " + newQuota); - } - if (mRemainingAlertQuota == newQuota) return; - - mRemainingAlertQuota = newQuota; - if (mRemainingAlertQuota == 0) { - mLog.i("onAlertReached"); - if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); - } - } - - private void updateQuotaAndStatsFromSnapshot( - @NonNull final TetherStatsParcel[] tetherStatsList) { - long usedAlertQuota = 0; - for (TetherStatsParcel tetherStats : tetherStatsList) { - final Integer ifIndex = tetherStats.ifIndex; - final ForwardedStats curr = new ForwardedStats(tetherStats); - final ForwardedStats base = mStats.get(ifIndex); - final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr; - usedAlertQuota += diff.rxBytes + diff.txBytes; - - // Update the local cache for counting tether stats delta. - mStats.put(ifIndex, curr); - - // Update the accumulated tether stats delta to the stats provider for the service - // querying. - if (mStatsProvider != null) { - try { - mStatsProvider.accumulateDiff( - buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff), - buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff)); - } catch (ArrayIndexOutOfBoundsException e) { - Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index " - + ifIndex + " : ", e); - } - } - } - - if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { - // Trim to zero if overshoot. - final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); - updateAlertQuota(newQuota); - } - - // TODO: Count the used limit quota for notifying data limit reached. - } - - private void updateForwardedStatsFromNetd() { - final TetherStatsParcel[] tetherStatsList; - try { - // The reported tether stats are total data usage for all currently-active upstream - // interfaces since tethering start. - tetherStatsList = mNetd.tetherOffloadGetStats(); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e("Problem fetching tethering stats: ", e); - return; - } - updateQuotaAndStatsFromSnapshot(tetherStatsList); - } - - @VisibleForTesting - int getPollingInterval() { - // The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. - // Ignore the config value is less than the minimum polling interval. Note that the - // minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does. - // TODO: Perhaps define a minimum polling interval constant. - final TetheringConfiguration config = mDeps.getTetherConfig(); - final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0; - return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval); - } - - private void maybeSchedulePollingStats() { - if (!mPollingStarted) return; - - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - - mHandler.postDelayed(mScheduledPollingTask, getPollingInterval()); - } - - // Return forwarding rule map. This is used for testing only. - // Note that this can be only called on handler thread. - @NonNull - @VisibleForTesting - final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> - getForwardingRulesForTesting() { - return mIpv6ForwardingRules; - } - - // Return upstream interface name map. This is used for testing only. - // Note that this can be only called on handler thread. - @NonNull - @VisibleForTesting - final SparseArray<String> getInterfaceNamesForTesting() { - return mInterfaceNames; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java deleted file mode 100644 index 8a96988ae1d1..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.TetheringManager.TETHERING_WIFI; - -import android.net.MacAddress; -import android.net.TetheredClient; -import android.net.TetheredClient.AddressInfo; -import android.net.ip.IpServer; -import android.net.wifi.WifiClient; -import android.os.SystemClock; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Tracker for clients connected to downstreams. - * - * <p>This class is not thread safe, it is intended to be used only from the tethering handler - * thread. - */ -public class ConnectedClientsTracker { - private final Clock mClock; - - @NonNull - private List<WifiClient> mLastWifiClients = Collections.emptyList(); - @NonNull - private List<TetheredClient> mLastTetheredClients = Collections.emptyList(); - - @VisibleForTesting - static class Clock { - public long elapsedRealtime() { - return SystemClock.elapsedRealtime(); - } - } - - public ConnectedClientsTracker() { - this(new Clock()); - } - - @VisibleForTesting - ConnectedClientsTracker(Clock clock) { - mClock = clock; - } - - /** - * Update the tracker with new connected clients. - * - * <p>The new list can be obtained through {@link #getLastTetheredClients()}. - * @param ipServers The IpServers used to assign addresses to clients. - * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last - * update. - * @return True if the list of clients changed since the last calculation. - */ - public boolean updateConnectedClients( - Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) { - final long now = mClock.elapsedRealtime(); - - if (wifiClients != null) { - mLastWifiClients = wifiClients; - } - final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients); - - // Build the list of non-expired leases from all IpServers, grouped by mac address - final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>(); - for (IpServer server : ipServers) { - for (TetheredClient client : server.getAllLeases()) { - if (client.getTetheringType() == TETHERING_WIFI - && !wifiClientMacs.contains(client.getMacAddress())) { - // Skip leases of WiFi clients that are not (or no longer) L2-connected - continue; - } - final TetheredClient prunedClient = pruneExpired(client, now); - if (prunedClient == null) continue; // All addresses expired - - addLease(clientsMap, prunedClient); - } - } - - // TODO: add IPv6 addresses from netlink - - // Add connected WiFi clients that do not have any known address - for (MacAddress client : wifiClientMacs) { - if (clientsMap.containsKey(client)) continue; - clientsMap.put(client, new TetheredClient( - client, Collections.emptyList() /* addresses */, TETHERING_WIFI)); - } - - final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values()); - final boolean clientsChanged = clients.size() != mLastTetheredClients.size() - || !clients.containsAll(mLastTetheredClients); - mLastTetheredClients = Collections.unmodifiableList(new ArrayList<>(clients)); - return clientsChanged; - } - - private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) { - final TetheredClient aggregateClient = clientsMap.getOrDefault( - lease.getMacAddress(), lease); - if (aggregateClient == lease) { - // This is the first lease with this mac address - clientsMap.put(lease.getMacAddress(), lease); - return; - } - - // Only add the address info; this assumes that the tethering type is the same when the mac - // address is the same. If a client is connected through different tethering types with the - // same mac address, connected clients callbacks will report all of its addresses under only - // one of these tethering types. This keeps the API simple considering that such a scenario - // would really be a rare edge case. - clientsMap.put(lease.getMacAddress(), aggregateClient.addAddresses(lease)); - } - - /** - * Get the last list of tethered clients, as calculated in {@link #updateConnectedClients}. - * - * <p>The returned list is immutable. - */ - @NonNull - public List<TetheredClient> getLastTetheredClients() { - return mLastTetheredClients; - } - - private static boolean hasExpiredAddress(List<AddressInfo> addresses, long now) { - for (AddressInfo info : addresses) { - if (info.getExpirationTime() <= now) { - return true; - } - } - return false; - } - - @Nullable - private static TetheredClient pruneExpired(TetheredClient client, long now) { - final List<AddressInfo> addresses = client.getAddresses(); - if (addresses.size() == 0) return null; - if (!hasExpiredAddress(addresses, now)) return client; - - final ArrayList<AddressInfo> newAddrs = new ArrayList<>(addresses.size() - 1); - for (AddressInfo info : addresses) { - if (info.getExpirationTime() > now) { - newAddrs.add(info); - } - } - - if (newAddrs.size() == 0) { - return null; - } - return new TetheredClient(client.getMacAddress(), newAddrs, client.getTetheringType()); - } - - @NonNull - private static Set<MacAddress> getClientMacs(@NonNull List<WifiClient> clients) { - final Set<MacAddress> macs = new HashSet<>(clients.size()); - for (WifiClient c : clients) { - macs.add(c.getMacAddress()); - } - return macs; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java deleted file mode 100644 index bb7322f2a0d2..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.util.SharedLog; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Parcel; -import android.os.PersistableBundle; -import android.os.ResultReceiver; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; -import android.util.SparseIntArray; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.PrintWriter; -import java.util.BitSet; - -/** - * Re-check tethering provisioning for enabled downstream tether types. - * Reference TetheringManager.TETHERING_{@code *} for each tether type. - * - * All methods of this class must be accessed from the thread of tethering - * state machine. - * @hide - */ -public class EntitlementManager { - private static final String TAG = EntitlementManager.class.getSimpleName(); - private static final boolean DBG = false; - - @VisibleForTesting - protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; - private static final String ACTION_PROVISIONING_ALARM = - "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; - - private final ComponentName mSilentProvisioningService; - private static final int MS_PER_HOUR = 60 * 60 * 1000; - private static final int DUMP_TIMEOUT = 10_000; - - // The BitSet is the bit map of each enabled downstream types, ex: - // {@link TetheringManager.TETHERING_WIFI} - // {@link TetheringManager.TETHERING_USB} - // {@link TetheringManager.TETHERING_BLUETOOTH} - private final BitSet mCurrentDownstreams; - private final BitSet mExemptedDownstreams; - private final Context mContext; - private final SharedLog mLog; - private final SparseIntArray mEntitlementCacheValue; - private final Handler mHandler; - // Key: TetheringManager.TETHERING_*(downstream). - // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). - private final SparseIntArray mCurrentEntitlementResults; - private final Runnable mPermissionChangeCallback; - private PendingIntent mProvisioningRecheckAlarm; - private boolean mLastCellularUpstreamPermitted = true; - private boolean mUsingCellularAsUpstream = false; - private boolean mNeedReRunProvisioningUi = false; - private OnUiEntitlementFailedListener mListener; - private TetheringConfigurationFetcher mFetcher; - - public EntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - mContext = ctx; - mLog = log.forSubComponent(TAG); - mCurrentDownstreams = new BitSet(); - mExemptedDownstreams = new BitSet(); - mCurrentEntitlementResults = new SparseIntArray(); - mEntitlementCacheValue = new SparseIntArray(); - mPermissionChangeCallback = callback; - mHandler = h; - mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), - null, mHandler); - mSilentProvisioningService = ComponentName.unflattenFromString( - mContext.getResources().getString(R.string.config_wifi_tether_enable)); - } - - public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { - mListener = listener; - } - - /** Callback fired when UI entitlement failed. */ - public interface OnUiEntitlementFailedListener { - /** - * Ui entitlement check fails in |downstream|. - * - * @param downstream tethering type from TetheringManager.TETHERING_{@code *}. - */ - void onUiEntitlementFailed(int downstream); - } - - public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) { - mFetcher = fetcher; - } - - /** Interface to fetch TetheringConfiguration. */ - public interface TetheringConfigurationFetcher { - /** - * Fetch current tethering configuration. This will be called to ensure whether entitlement - * check is needed. - * @return TetheringConfiguration instance. - */ - TetheringConfiguration fetchTetheringConfiguration(); - } - - /** - * Check if cellular upstream is permitted. - */ - public boolean isCellularUpstreamPermitted() { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - - return isCellularUpstreamPermitted(config); - } - - private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { - if (!isTetherProvisioningRequired(config)) return true; - - // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular - // upstream should not be enabled. Enable cellular upstream for exempted downstreams only - // when there is no non-exempted downstream. - if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty(); - - return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; - } - - /** - * Set exempted downstream type. If there is only exempted downstream type active, - * corresponding entitlement check will not be run and cellular upstream will be permitted - * by default. If a privileged app enables tethering without a provisioning check, and then - * another app enables tethering of the same type but does not disable the provisioning check, - * then the downstream immediately loses exempt status and a provisioning check is run. - * If any non-exempted downstream type is active, the cellular upstream will be gated by the - * result of entitlement check from non-exempted downstreams. If entitlement check is still - * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any - * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled. - */ - public void setExemptedDownstreamType(final int type) { - mExemptedDownstreams.set(type, true); - } - - /** - * This is called when tethering starts. - * Launch provisioning app if upstream is cellular. - * - * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *} - * @param showProvisioningUi a boolean indicating whether to show the - * provisioning app UI if there is one. - */ - public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { - if (!isValidDownstreamType(downstreamType)) return; - - mCurrentDownstreams.set(downstreamType, true); - - mExemptedDownstreams.set(downstreamType, false); - - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - if (!isTetherProvisioningRequired(config)) return; - - // If upstream is not cellular, provisioning app would not be launched - // till upstream change to cellular. - if (mUsingCellularAsUpstream) { - if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config); - } else { - runSilentTetherProvisioning(downstreamType, config); - } - mNeedReRunProvisioningUi = false; - } else { - mNeedReRunProvisioningUi |= showProvisioningUi; - } - } - - /** - * Tell EntitlementManager that a given type of tethering has been disabled - * - * @param type tethering type from TetheringManager.TETHERING_{@code *} - */ - public void stopProvisioningIfNeeded(int downstreamType) { - if (!isValidDownstreamType(downstreamType)) return; - - mCurrentDownstreams.set(downstreamType, false); - // There are lurking bugs where the notion of "provisioning required" or - // "tethering supported" may change without without tethering being notified properly. - // Remove the mapping all the time no matter provisioning is required or not. - removeDownstreamMapping(downstreamType); - mExemptedDownstreams.set(downstreamType, false); - } - - /** - * Notify EntitlementManager if upstream is cellular or not. - * - * @param isCellular whether tethering upstream is cellular. - */ - public void notifyUpstream(boolean isCellular) { - if (DBG) { - mLog.i("notifyUpstream: " + isCellular - + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted - + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi); - } - mUsingCellularAsUpstream = isCellular; - - if (mUsingCellularAsUpstream) { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - maybeRunProvisioning(config); - } - } - - /** Run provisioning if needed */ - public void maybeRunProvisioning() { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - maybeRunProvisioning(config); - } - - private void maybeRunProvisioning(final TetheringConfiguration config) { - if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) { - return; - } - - // Whenever any entitlement value changes, all downstreams will re-evaluate whether they - // are allowed. Therefore even if the silent check here ends in a failure and the UI later - // yields success, then the downstream that got a failure will re-evaluate as a result of - // the change and get the new correct value. - for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0; - downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) { - if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { - if (mNeedReRunProvisioningUi) { - mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config); - } else { - runSilentTetherProvisioning(downstream, config); - } - } - } - } - - /** - * Check if the device requires a provisioning check in order to enable tethering. - * - * @param config an object that encapsulates the various tethering configuration elements. - * @return a boolean - {@code true} indicating tether provisioning is required by the carrier. - */ - @VisibleForTesting - protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) { - if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) - || config.provisioningApp.length == 0) { - return false; - } - if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) { - return false; - } - return (config.provisioningApp.length == 2); - } - - /** - * Re-check tethering provisioning for all enabled tether types. - * Reference TetheringManager.TETHERING_{@code *} for each tether type. - * - * @param config an object that encapsulates the various tethering configuration elements. - * Note: this method is only called from @{link Tethering.TetherMainSM} on the handler thread. - * If there are new callers from different threads, the logic should move to - * @{link Tethering.TetherMainSM} handler to avoid race conditions. - */ - public void reevaluateSimCardProvisioning(final TetheringConfiguration config) { - if (DBG) mLog.i("reevaluateSimCardProvisioning"); - - if (!mHandler.getLooper().isCurrentThread()) { - // Except for test, this log should not appear in normal flow. - mLog.log("reevaluateSimCardProvisioning() don't run in TetherMainSM thread"); - } - mEntitlementCacheValue.clear(); - mCurrentEntitlementResults.clear(); - - // TODO: refine provisioning check to isTetherProvisioningRequired() ?? - if (!config.hasMobileHotspotProvisionApp() - || carrierConfigAffirmsEntitlementCheckNotRequired(config)) { - evaluateCellularPermission(config); - return; - } - - if (mUsingCellularAsUpstream) { - maybeRunProvisioning(config); - } - } - - /** - * Get carrier configuration bundle. - * @param config an object that encapsulates the various tethering configuration elements. - * */ - public PersistableBundle getCarrierConfig(final TetheringConfiguration config) { - final CarrierConfigManager configManager = (CarrierConfigManager) mContext - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager == null) return null; - - final PersistableBundle carrierConfig = configManager.getConfigForSubId( - config.activeDataSubId); - - if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) { - return carrierConfig; - } - - return null; - } - - // The logic here is aimed solely at confirming that a CarrierConfig exists - // and affirms that entitlement checks are not required. - // - // TODO: find a better way to express this, or alter the checking process - // entirely so that this is more intuitive. - private boolean carrierConfigAffirmsEntitlementCheckNotRequired( - final TetheringConfiguration config) { - // Check carrier config for entitlement checks - final PersistableBundle carrierConfig = getCarrierConfig(config); - if (carrierConfig == null) return false; - - // A CarrierConfigManager was found and it has a config. - final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( - CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); - return !isEntitlementCheckRequired; - } - - /** - * Run no UI tethering provisioning check. - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param subId default data subscription ID. - */ - @VisibleForTesting - protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { - if (DBG) mLog.i("runSilentTetherProvisioning: " + type); - // For silent provisioning, settings would stop tethering when entitlement fail. - ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); - - Intent intent = new Intent(); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_RUN_PROVISION, true); - intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); - intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); - intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); - intent.setComponent(mSilentProvisioningService); - // Only admin user can change tethering and SilentTetherProvisioning don't need to - // show UI, it is fine to always start setting's background service as system user. - mContext.startService(intent); - return intent; - } - - private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { - ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, config, receiver); - } - - /** - * Run the UI-enabled tethering provisioning check. - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param subId default data subscription ID. - * @param receiver to receive entitlement check result. - */ - @VisibleForTesting - protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, - ResultReceiver receiver) { - if (DBG) mLog.i("runUiTetherProvisioning: " + type); - - Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); - intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // Only launch entitlement UI for system user. Entitlement UI should not appear for other - // user because only admin user is allowed to change tethering. - mContext.startActivity(intent); - return intent; - } - - // Not needed to check if this don't run on the handler thread because it's private. - private void scheduleProvisioningRechecks(final TetheringConfiguration config) { - if (mProvisioningRecheckAlarm == null) { - final int period = config.provisioningCheckPeriod; - if (period <= 0) return; - - Intent intent = new Intent(ACTION_PROVISIONING_ALARM); - mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 0); - AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( - Context.ALARM_SERVICE); - long periodMs = period * MS_PER_HOUR; - long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs; - alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs, - mProvisioningRecheckAlarm); - } - } - - private void cancelTetherProvisioningRechecks() { - if (mProvisioningRecheckAlarm != null) { - AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( - Context.ALARM_SERVICE); - alarmManager.cancel(mProvisioningRecheckAlarm); - mProvisioningRecheckAlarm = null; - } - } - - private void evaluateCellularPermission(final TetheringConfiguration config) { - final boolean permitted = isCellularUpstreamPermitted(config); - - if (DBG) { - mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted - + " to " + permitted); - } - - if (mLastCellularUpstreamPermitted != permitted) { - mLog.log("Cellular permission change: " + permitted); - mPermissionChangeCallback.run(); - } - // Only schedule periodic re-check when tether is provisioned - // and the result is ok. - if (permitted && mCurrentEntitlementResults.size() > 0) { - scheduleProvisioningRechecks(config); - } else { - cancelTetherProvisioningRechecks(); - } - mLastCellularUpstreamPermitted = permitted; - } - - /** - * Add the mapping between provisioning result and tethering type. - * Notify UpstreamNetworkMonitor if Cellular permission changes. - * - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param resultCode Provisioning result - */ - protected void addDownstreamMapping(int type, int resultCode) { - mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode - + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type)); - if (!mCurrentDownstreams.get(type)) return; - - mCurrentEntitlementResults.put(type, resultCode); - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - evaluateCellularPermission(config); - } - - /** - * Remove the mapping for input tethering type. - * @param type tethering type from TetheringManager.TETHERING_{@code *} - */ - protected void removeDownstreamMapping(int type) { - mLog.i("removeDownstreamMapping: " + type); - mCurrentEntitlementResults.delete(type); - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - evaluateCellularPermission(config); - } - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) { - mLog.log("Received provisioning alarm"); - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - reevaluateSimCardProvisioning(config); - } - } - }; - - private static boolean isValidDownstreamType(int type) { - switch (type) { - case TETHERING_BLUETOOTH: - case TETHERING_ETHERNET: - case TETHERING_USB: - case TETHERING_WIFI: - return true; - default: - return false; - } - } - - /** - * Dump the infromation of EntitlementManager. - * @param pw {@link PrintWriter} is used to print formatted - */ - public void dump(PrintWriter pw) { - final ConditionVariable mWaiting = new ConditionVariable(); - mHandler.post(() -> { - pw.print("isCellularUpstreamPermitted: "); - pw.println(isCellularUpstreamPermitted()); - for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0; - type = mCurrentDownstreams.nextSetBit(type + 1)) { - pw.print("Type: "); - pw.print(typeString(type)); - if (mCurrentEntitlementResults.indexOfKey(type) > -1) { - pw.print(", Value: "); - pw.println(errorString(mCurrentEntitlementResults.get(type))); - } else { - pw.println(", Value: empty"); - } - } - mWaiting.open(); - }); - if (!mWaiting.block(DUMP_TIMEOUT)) { - pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms"); - } - pw.print("Exempted: ["); - for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0; - type = mExemptedDownstreams.nextSetBit(type + 1)) { - pw.print(typeString(type)); - pw.print(", "); - } - pw.println("]"); - } - - private static String typeString(int type) { - switch (type) { - case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH"; - case TETHERING_INVALID: return "TETHERING_INVALID"; - case TETHERING_USB: return "TETHERING_USB"; - case TETHERING_WIFI: return "TETHERING_WIFI"; - default: - return String.format("TETHERING UNKNOWN TYPE (%d)", type); - } - } - - private static String errorString(int value) { - switch (value) { - case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; - case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR"; - case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED"; - default: - return String.format("UNKNOWN ERROR (%d)", value); - } - } - - private ResultReceiver buildProxyReceiver(int type, boolean notifyFail, - final ResultReceiver receiver) { - ResultReceiver rr = new ResultReceiver(mHandler) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - int updatedCacheValue = updateEntitlementCacheValue(type, resultCode); - addDownstreamMapping(type, updatedCacheValue); - if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) { - mListener.onUiEntitlementFailed(type); - } - if (receiver != null) receiver.send(updatedCacheValue, null); - } - }; - - return writeToParcel(rr); - } - - // Instances of ResultReceiver need to be public classes for remote processes to be able - // to load them (otherwise, ClassNotFoundException). For private classes, this method - // performs a trick : round-trip parceling any instance of ResultReceiver will return a - // vanilla instance of ResultReceiver sharing the binder token with the original receiver. - // The binder token has a reference to the original instance of the private class and will - // still call its methods, and can be sent over. However it cannot be used for anything - // else than sending over a Binder call. - // While round-trip parceling is not great, there is currently no other way of generating - // a vanilla instance of ResultReceiver because all its fields are private. - private ResultReceiver writeToParcel(final ResultReceiver receiver) { - Parcel parcel = Parcel.obtain(); - receiver.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel); - parcel.recycle(); - return receiverForSending; - } - - /** - * Update the last entitlement value to internal cache - * - * @param type tethering type from TetheringManager.TETHERING_{@code *} - * @param resultCode last entitlement value - * @return the last updated entitlement value - */ - private int updateEntitlementCacheValue(int type, int resultCode) { - if (DBG) { - mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode); - } - if (resultCode == TETHER_ERROR_NO_ERROR) { - mEntitlementCacheValue.put(type, resultCode); - return resultCode; - } else { - mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED); - return TETHER_ERROR_PROVISIONING_FAILED; - } - } - - /** Get the last value of the tethering entitlement check. */ - public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, - boolean showEntitlementUi) { - if (!isValidDownstreamType(downstream)) { - receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); - return; - } - - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - if (!isTetherProvisioningRequired(config)) { - receiver.send(TETHER_ERROR_NO_ERROR, null); - return; - } - - final int cacheValue = mEntitlementCacheValue.get( - downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN); - if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) { - receiver.send(cacheValue, null); - } else { - ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config, proxy); - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java deleted file mode 100644 index f3dcaa2529e7..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.ip.IpServer; -import android.net.util.NetworkConstants; -import android.net.util.SharedLog; -import android.util.Log; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.Random; - - -/** - * IPv6 tethering is rather different from IPv4 owing to the absence of NAT. - * This coordinator is responsible for evaluating the dedicated prefixes - * assigned to the device and deciding how to divvy them up among downstream - * interfaces. - * - * @hide - */ -public class IPv6TetheringCoordinator { - private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - private static class Downstream { - public final IpServer ipServer; - public final int mode; // IpServer.STATE_* - // Used to append to a ULA /48, constructing a ULA /64 for local use. - public final short subnetId; - - Downstream(IpServer ipServer, int mode, short subnetId) { - this.ipServer = ipServer; - this.mode = mode; - this.subnetId = subnetId; - } - } - - private final ArrayList<IpServer> mNotifyList; - private final SharedLog mLog; - // NOTE: mActiveDownstreams is a list and not a hash data structure because - // we keep active downstreams in arrival order. This is done so /64s can - // be parceled out on a "first come, first served" basis and a /64 used by - // a downstream that is no longer active can be redistributed to any next - // waiting active downstream (again, in arrival order). - private final LinkedList<Downstream> mActiveDownstreams; - private final byte[] mUniqueLocalPrefix; - private short mNextSubnetId; - private UpstreamNetworkState mUpstreamNetworkState; - - public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) { - mNotifyList = notifyList; - mLog = log.forSubComponent(TAG); - mActiveDownstreams = new LinkedList<>(); - mUniqueLocalPrefix = generateUniqueLocalPrefix(); - mNextSubnetId = 0; - } - - /** Add active downstream to ipv6 tethering candidate list. */ - public void addActiveDownstream(IpServer downstream, int mode) { - if (findDownstream(downstream) == null) { - // Adding a new downstream appends it to the list. Adding a - // downstream a second time without first removing it has no effect. - // We never change the mode of a downstream except by first removing - // it and then re-adding it (with its new mode specified); - if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) { - // Make sure subnet IDs are always positive. They are appended - // to a ULA /48 to make a ULA /64 for local use. - mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1); - } - updateIPv6TetheringInterfaces(); - } - } - - /** Remove downstream from ipv6 tethering candidate list. */ - public void removeActiveDownstream(IpServer downstream) { - stopIPv6TetheringOn(downstream); - if (mActiveDownstreams.remove(findDownstream(downstream))) { - updateIPv6TetheringInterfaces(); - } - - // When tethering is stopping we can reset the subnet counter. - if (mNotifyList.isEmpty()) { - if (!mActiveDownstreams.isEmpty()) { - Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty."); - } - mNextSubnetId = 0; - } - } - - /** - * Call when UpstreamNetworkState may be changed. - * If upstream has ipv6 for tethering, update this new UpstreamNetworkState - * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces. - */ - public void updateUpstreamNetworkState(UpstreamNetworkState ns) { - if (VDBG) { - Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); - } - if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { - stopIPv6TetheringOnAllInterfaces(); - setUpstreamNetworkState(null); - return; - } - - if (mUpstreamNetworkState != null - && !ns.network.equals(mUpstreamNetworkState.network)) { - stopIPv6TetheringOnAllInterfaces(); - } - - setUpstreamNetworkState(ns); - updateIPv6TetheringInterfaces(); - } - - private void stopIPv6TetheringOnAllInterfaces() { - for (IpServer ipServer : mNotifyList) { - stopIPv6TetheringOn(ipServer); - } - } - - private void setUpstreamNetworkState(UpstreamNetworkState ns) { - if (ns == null) { - mUpstreamNetworkState = null; - } else { - // Make a deep copy of the parts we need. - mUpstreamNetworkState = new UpstreamNetworkState( - new LinkProperties(ns.linkProperties), - new NetworkCapabilities(ns.networkCapabilities), - new Network(ns.network)); - } - - mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); - } - - private void updateIPv6TetheringInterfaces() { - for (IpServer ipServer : mNotifyList) { - final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); - ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp); - break; - } - } - - private int getTtlAdjustment() { - if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) { - return 0; - } - - // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1 - // for carrier requirement. - if (mUpstreamNetworkState.networkCapabilities.hasTransport( - NetworkCapabilities.TRANSPORT_CELLULAR)) { - return -1; - } - - // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary - // distinction between tethered and untethered traffic. - return 1; - } - - private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { - final Downstream ds = findDownstream(ipServer); - if (ds == null) return null; - - if (ds.mode == IpServer.STATE_LOCAL_ONLY) { - // Build a Unique Locally-assigned Prefix configuration. - return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId); - } - - // This downstream is in IpServer.STATE_TETHERED mode. - if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) { - return null; - } - - // NOTE: Here, in future, we would have policies to decide how to divvy - // up the available dedicated prefixes among downstream interfaces. - // At this time we have no such mechanism--we only support tethering - // IPv6 toward the oldest (first requested) active downstream. - - final Downstream currentActive = mActiveDownstreams.peek(); - if (currentActive != null && currentActive.ipServer == ipServer) { - final LinkProperties lp = getIPv6OnlyLinkProperties( - mUpstreamNetworkState.linkProperties); - if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) { - return lp; - } - } - - return null; - } - - Downstream findDownstream(IpServer ipServer) { - for (Downstream ds : mActiveDownstreams) { - if (ds.ipServer == ipServer) return ds; - } - return null; - } - - private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { - final LinkProperties v6only = new LinkProperties(); - if (lp == null) { - return v6only; - } - - // NOTE: At this time we don't copy over any information about any - // stacked links. No current stacked link configuration has IPv6. - - v6only.setInterfaceName(lp.getInterfaceName()); - - v6only.setMtu(lp.getMtu()); - - for (LinkAddress linkAddr : lp.getLinkAddresses()) { - if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) { - v6only.addLinkAddress(linkAddr); - } - } - - for (RouteInfo routeInfo : lp.getRoutes()) { - final IpPrefix destination = routeInfo.getDestination(); - if ((destination.getAddress() instanceof Inet6Address) - && (destination.getPrefixLength() <= 64)) { - v6only.addRoute(routeInfo); - } - } - - for (InetAddress dnsServer : lp.getDnsServers()) { - if (isIPv6GlobalAddress(dnsServer)) { - // For now we include ULAs. - v6only.addDnsServer(dnsServer); - } - } - - v6only.setDomains(lp.getDomains()); - - return v6only; - } - - // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we - // announce our own IPv6 address as DNS server. - private static boolean isIPv6GlobalAddress(InetAddress ip) { - return (ip instanceof Inet6Address) - && !ip.isAnyLocalAddress() - && !ip.isLoopbackAddress() - && !ip.isLinkLocalAddress() - && !ip.isSiteLocalAddress() - && !ip.isMulticastAddress(); - } - - private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) { - final LinkProperties lp = new LinkProperties(); - - final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48); - lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST)); - - final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64); - // Because this is a locally-generated ULA, we don't have an upstream - // address. But because the downstream IP address management code gets - // its prefix from the upstream's IP address, we create a fake one here. - lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64)); - - lp.setMtu(NetworkConstants.ETHER_MTU); - return lp; - } - - private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) { - final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length); - bytes[7] = (byte) (subnetId >> 8); - bytes[8] = (byte) subnetId; - final InetAddress addr; - try { - addr = InetAddress.getByAddress(bytes); - } catch (UnknownHostException e) { - throw new IllegalStateException("Invalid address length: " + bytes.length, e); - } - return new IpPrefix(addr, prefixlen); - } - - // Generates a Unique Locally-assigned Prefix: - // - // https://tools.ietf.org/html/rfc4193#section-3.1 - // - // The result is a /48 that can be used for local-only communications. - private static byte[] generateUniqueLocalPrefix() { - final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte - (new Random()).nextBytes(ulp); - - final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN); - in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1 - - return in6addr; - } - - private static String toDebugString(UpstreamNetworkState ns) { - if (ns == null) { - return "UpstreamNetworkState{null}"; - } - return ns.toString(); - } - - private static void stopIPv6TetheringOn(IpServer ipServer) { - ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java deleted file mode 100644 index 88c77b07e7e3..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; -import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; - -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.usage.NetworkStatsManager; -import android.content.ContentResolver; -import android.net.InetAddresses; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; -import android.net.RouteInfo; -import android.net.netlink.ConntrackMessage; -import android.net.netlink.NetlinkConstants; -import android.net.netlink.NetlinkSocket; -import android.net.netstats.provider.NetworkStatsProvider; -import android.net.util.SharedLog; -import android.os.Handler; -import android.provider.Settings; -import android.system.ErrnoException; -import android.system.OsConstants; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * A class to encapsulate the business logic of programming the tethering - * hardware offload interface. - * - * @hide - */ -public class OffloadController { - private static final String TAG = OffloadController.class.getSimpleName(); - private static final boolean DBG = false; - private static final String ANYIP = "0.0.0.0"; - private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); - - @VisibleForTesting - enum StatsType { - STATS_PER_IFACE, - STATS_PER_UID, - } - - private enum UpdateType { IF_NEEDED, FORCE }; - - private final Handler mHandler; - private final OffloadHardwareInterface mHwInterface; - private final ContentResolver mContentResolver; - @Nullable - private final OffloadTetheringStatsProvider mStatsProvider; - private final SharedLog mLog; - private final HashMap<String, LinkProperties> mDownstreams; - private boolean mConfigInitialized; - private boolean mControlInitialized; - private LinkProperties mUpstreamLinkProperties; - // The complete set of offload-exempt prefixes passed in via Tethering from - // all upstream and downstream sources. - private Set<IpPrefix> mExemptPrefixes; - // A strictly "smaller" set of prefixes, wherein offload-approved prefixes - // (e.g. downstream on-link prefixes) have been removed and replaced with - // prefixes representing only the locally-assigned IP addresses. - private Set<String> mLastLocalPrefixStrs; - - // Maps upstream interface names to offloaded traffic statistics. - // Always contains the latest value received from the hardware for each interface, regardless of - // whether offload is currently running on that interface. - private ConcurrentHashMap<String, ForwardedStats> mForwardedStats = - new ConcurrentHashMap<>(16, 0.75F, 1); - - // Maps upstream interface names to interface quotas. - // Always contains the latest value received from the framework for each interface, regardless - // of whether offload is currently running (or is even supported) on that interface. Only - // includes upstream interfaces that have a quota set. - private HashMap<String, Long> mInterfaceQuotas = new HashMap<>(); - - // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert - // quota is interface independent and global for tether offload. Note that this is only - // accessed on the handler thread and in the constructor. - private long mRemainingAlertQuota = QUOTA_UNLIMITED; - // Runnable that used to schedule the next stats poll. - private final Runnable mScheduledPollingTask = () -> { - updateStatsForCurrentUpstream(); - maybeSchedulePollingStats(); - }; - - private int mNatUpdateCallbacksReceived; - private int mNatUpdateNetlinkErrors; - - @NonNull - private final Dependencies mDeps; - - // TODO: Put more parameters in constructor into dependency object. - interface Dependencies { - @NonNull - TetheringConfiguration getTetherConfig(); - } - - public OffloadController(Handler h, OffloadHardwareInterface hwi, - ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log, - @NonNull Dependencies deps) { - mHandler = h; - mHwInterface = hwi; - mContentResolver = contentResolver; - mLog = log.forSubComponent(TAG); - mDownstreams = new HashMap<>(); - mExemptPrefixes = new HashSet<>(); - mLastLocalPrefixStrs = new HashSet<>(); - OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider(); - try { - nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider); - } catch (RuntimeException e) { - Log.wtf(TAG, "Cannot register offload stats provider: " + e); - provider = null; - } - mStatsProvider = provider; - mDeps = deps; - } - - /** Start hardware offload. */ - public boolean start() { - if (started()) return true; - - if (isOffloadDisabled()) { - mLog.i("tethering offload disabled"); - return false; - } - - if (!mConfigInitialized) { - mConfigInitialized = mHwInterface.initOffloadConfig(); - if (!mConfigInitialized) { - mLog.i("tethering offload config not supported"); - stop(); - return false; - } - } - - mControlInitialized = mHwInterface.initOffloadControl( - // OffloadHardwareInterface guarantees that these callback - // methods are called on the handler passed to it, which is the - // same as mHandler, as coordinated by the setup in Tethering. - new OffloadHardwareInterface.ControlCallback() { - @Override - public void onStarted() { - if (!started()) return; - mLog.log("onStarted"); - } - - @Override - public void onStoppedError() { - if (!started()) return; - mLog.log("onStoppedError"); - } - - @Override - public void onStoppedUnsupported() { - if (!started()) return; - mLog.log("onStoppedUnsupported"); - // Poll for statistics and trigger a sweep of tethering - // stats by observers. This might not succeed, but it's - // worth trying anyway. We need to do this because from - // this point on we continue with software forwarding, - // and we need to synchronize stats and limits between - // software and hardware forwarding. - updateStatsForAllUpstreams(); - if (mStatsProvider != null) mStatsProvider.pushTetherStats(); - } - - @Override - public void onSupportAvailable() { - if (!started()) return; - mLog.log("onSupportAvailable"); - - // [1] Poll for statistics and trigger a sweep of stats - // by observers. We need to do this to ensure that any - // limits set take into account any software tethering - // traffic that has been happening in the meantime. - updateStatsForAllUpstreams(); - if (mStatsProvider != null) mStatsProvider.pushTetherStats(); - // [2] (Re)Push all state. - computeAndPushLocalPrefixes(UpdateType.FORCE); - pushAllDownstreamState(); - pushUpstreamParameters(null); - } - - @Override - public void onStoppedLimitReached() { - if (!started()) return; - mLog.log("onStoppedLimitReached"); - - // We cannot reliably determine on which interface the limit was reached, - // because the HAL interface does not specify it. We cannot just use the - // current upstream, because that might have changed since the time that - // the HAL queued the callback. - // TODO: rev the HAL so that it provides an interface name. - - updateStatsForCurrentUpstream(); - if (mStatsProvider != null) { - mStatsProvider.pushTetherStats(); - // Push stats to service does not cause the service react to it - // immediately. Inform the service about limit reached. - mStatsProvider.notifyLimitReached(); - } - } - - @Override - public void onNatTimeoutUpdate(int proto, - String srcAddr, int srcPort, - String dstAddr, int dstPort) { - if (!started()) return; - updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort); - } - }); - - final boolean isStarted = started(); - if (!isStarted) { - mLog.i("tethering offload control not supported"); - stop(); - } else { - mLog.log("tethering offload started"); - mNatUpdateCallbacksReceived = 0; - mNatUpdateNetlinkErrors = 0; - maybeSchedulePollingStats(); - } - return isStarted; - } - - /** Stop hardware offload. */ - public void stop() { - // Completely stops tethering offload. After this method is called, it is no longer safe to - // call any HAL method, no callbacks from the hardware will be delivered, and any in-flight - // callbacks must be ignored. Offload may be started again by calling start(). - final boolean wasStarted = started(); - updateStatsForCurrentUpstream(); - mUpstreamLinkProperties = null; - mHwInterface.stopOffloadControl(); - mControlInitialized = false; - mConfigInitialized = false; - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - if (wasStarted) mLog.log("tethering offload stopped"); - } - - private boolean started() { - return mConfigInitialized && mControlInitialized; - } - - @VisibleForTesting - class OffloadTetheringStatsProvider extends NetworkStatsProvider { - // These stats must only ever be touched on the handler thread. - @NonNull - private NetworkStats mIfaceStats = new NetworkStats(0L, 0); - @NonNull - private NetworkStats mUidStats = new NetworkStats(0L, 0); - - /** - * A helper function that collect tether stats from local hashmap. Note that this does not - * invoke binder call. - */ - @VisibleForTesting - @NonNull - NetworkStats getTetherStats(@NonNull StatsType how) { - NetworkStats stats = new NetworkStats(0L, 0); - final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; - - for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { - final ForwardedStats value = kv.getValue(); - final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L); - stats = stats.addEntry(entry); - } - - return stats; - } - - @Override - public void onSetLimit(String iface, long quotaBytes) { - // Listen for all iface is necessary since upstream might be changed after limit - // is set. - mHandler.post(() -> { - final Long curIfaceQuota = mInterfaceQuotas.get(iface); - - // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE, - // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not - // useful to set it multiple times. - // Otherwise, the quota needs to be updated to tell HAL to re-count from now even - // if the quota is the same as the existing one. - if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return; - - if (quotaBytes == QUOTA_UNLIMITED) { - mInterfaceQuotas.remove(iface); - } else { - mInterfaceQuotas.put(iface, quotaBytes); - } - maybeUpdateDataLimit(iface); - }); - } - - /** - * Push stats to service, but does not cause a force polling. Note that this can only be - * called on the handler thread. - */ - public void pushTetherStats() { - // TODO: remove the accumulated stats and report the diff from HAL directly. - final NetworkStats ifaceDiff = - getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats); - final NetworkStats uidDiff = - getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats); - try { - notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff); - mIfaceStats = mIfaceStats.add(ifaceDiff); - mUidStats = mUidStats.add(uidDiff); - } catch (RuntimeException e) { - mLog.e("Cannot report network stats: ", e); - } - } - - @Override - public void onRequestStatsUpdate(int token) { - // Do not attempt to update stats by querying the offload HAL - // synchronously from a different thread than the Handler thread. http://b/64771555. - mHandler.post(() -> { - updateStatsForCurrentUpstream(); - pushTetherStats(); - }); - } - - @Override - public void onSetAlert(long quotaBytes) { - // TODO: Ask offload HAL to notify alert without stopping traffic. - // Post it to handler thread since it access remaining quota bytes. - mHandler.post(() -> { - updateAlertQuota(quotaBytes); - maybeSchedulePollingStats(); - }); - } - } - - private String currentUpstreamInterface() { - return (mUpstreamLinkProperties != null) - ? mUpstreamLinkProperties.getInterfaceName() : null; - } - - private void maybeUpdateStats(String iface) { - if (TextUtils.isEmpty(iface)) { - return; - } - - // Always called on the handler thread. - // - // Use get()/put() instead of updating ForwardedStats in place because we can be called - // concurrently with getTetherStats. In combination with the guarantees provided by - // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of - // the stats for each interface, and does not observe partial writes where rxBytes is - // updated and txBytes is not. - ForwardedStats diff = mHwInterface.getForwardedStats(iface); - final long usedAlertQuota = diff.rxBytes + diff.txBytes; - ForwardedStats base = mForwardedStats.get(iface); - if (base != null) { - diff.add(base); - } - - // Update remaining alert quota if it is still positive. - if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { - // Trim to zero if overshoot. - final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); - updateAlertQuota(newQuota); - } - - mForwardedStats.put(iface, diff); - // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from - // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately. - } - - /** - * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()} - * callback when it reaches zero. This can be invoked either from service setting the alert, or - * {@code maybeUpdateStats} when updating stats. Note that this can be only called on - * handler thread. - * - * @param newQuota non-negative value to indicate the new quota, or - * {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no - * quota. - */ - private void updateAlertQuota(long newQuota) { - if (newQuota < QUOTA_UNLIMITED) { - throw new IllegalArgumentException("invalid quota value " + newQuota); - } - if (mRemainingAlertQuota == newQuota) return; - - mRemainingAlertQuota = newQuota; - if (mRemainingAlertQuota == 0) { - mLog.i("notifyAlertReached"); - if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); - } - } - - /** - * Schedule polling if needed, this will be stopped if offload has been - * stopped or remaining quota reaches zero or upstream is empty. - * Note that this can be only called on handler thread. - */ - private void maybeSchedulePollingStats() { - if (!isPollingStatsNeeded()) return; - - if (mHandler.hasCallbacks(mScheduledPollingTask)) { - mHandler.removeCallbacks(mScheduledPollingTask); - } - mHandler.postDelayed(mScheduledPollingTask, - mDeps.getTetherConfig().getOffloadPollInterval()); - } - - private boolean isPollingStatsNeeded() { - return started() && mRemainingAlertQuota > 0 - && !TextUtils.isEmpty(currentUpstreamInterface()) - && mDeps.getTetherConfig() != null - && mDeps.getTetherConfig().getOffloadPollInterval() - >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - } - - private boolean maybeUpdateDataLimit(String iface) { - // setDataLimit may only be called while offload is occurring on this upstream. - if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) { - return true; - } - - Long limit = mInterfaceQuotas.get(iface); - if (limit == null) { - limit = Long.MAX_VALUE; - } - - return mHwInterface.setDataLimit(iface, limit); - } - - private void updateStatsForCurrentUpstream() { - maybeUpdateStats(currentUpstreamInterface()); - } - - private void updateStatsForAllUpstreams() { - // In practice, there should only ever be a single digit number of - // upstream interfaces over the lifetime of an active tethering session. - // Roughly speaking, imagine a very ambitious one or two of each of the - // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ]. - for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { - maybeUpdateStats(kv.getKey()); - } - } - - /** Set current tethering upstream LinkProperties. */ - public void setUpstreamLinkProperties(LinkProperties lp) { - if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; - - final String prevUpstream = currentUpstreamInterface(); - - mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; - // Make sure we record this interface in the ForwardedStats map. - final String iface = currentUpstreamInterface(); - if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS); - - maybeSchedulePollingStats(); - - // TODO: examine return code and decide what to do if programming - // upstream parameters fails (probably just wait for a subsequent - // onOffloadEvent() callback to tell us offload is available again and - // then reapply all state). - computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); - pushUpstreamParameters(prevUpstream); - } - - /** Set local prefixes. */ - public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { - mExemptPrefixes = localPrefixes; - - if (!started()) return; - computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); - } - - /** Update current downstream LinkProperties. */ - public void notifyDownstreamLinkProperties(LinkProperties lp) { - final String ifname = lp.getInterfaceName(); - final LinkProperties oldLp = mDownstreams.put(ifname, new LinkProperties(lp)); - if (Objects.equals(oldLp, lp)) return; - - if (!started()) return; - pushDownstreamState(oldLp, lp); - } - - private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) { - final String ifname = newLp.getInterfaceName(); - final List<RouteInfo> oldRoutes = - (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST; - final List<RouteInfo> newRoutes = newLp.getRoutes(); - - // For each old route, if not in new routes: remove. - for (RouteInfo ri : oldRoutes) { - if (shouldIgnoreDownstreamRoute(ri)) continue; - if (!newRoutes.contains(ri)) { - mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString()); - } - } - - // For each new route, if not in old routes: add. - for (RouteInfo ri : newRoutes) { - if (shouldIgnoreDownstreamRoute(ri)) continue; - if (!oldRoutes.contains(ri)) { - mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString()); - } - } - } - - private void pushAllDownstreamState() { - for (LinkProperties lp : mDownstreams.values()) { - pushDownstreamState(null, lp); - } - } - - /** Remove downstream interface from offload hardware. */ - public void removeDownstreamInterface(String ifname) { - final LinkProperties lp = mDownstreams.remove(ifname); - if (lp == null) return; - - if (!started()) return; - - for (RouteInfo route : lp.getRoutes()) { - if (shouldIgnoreDownstreamRoute(route)) continue; - mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString()); - } - } - - private boolean isOffloadDisabled() { - final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled(); - return (Settings.Global.getInt( - mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0); - } - - private boolean pushUpstreamParameters(String prevUpstream) { - final String iface = currentUpstreamInterface(); - - if (TextUtils.isEmpty(iface)) { - final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null); - // Update stats after we've told the hardware to stop forwarding so - // we don't miss packets. - maybeUpdateStats(prevUpstream); - return rval; - } - - // A stacked interface cannot be an upstream for hardware offload. - // Consequently, we examine only the primary interface name, look at - // getAddresses() rather than getAllAddresses(), and check getRoutes() - // rather than getAllRoutes(). - final ArrayList<String> v6gateways = new ArrayList<>(); - String v4addr = null; - String v4gateway = null; - - for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) { - if (ip instanceof Inet4Address) { - v4addr = ip.getHostAddress(); - break; - } - } - - // Find the gateway addresses of all default routes of either address family. - for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) { - if (!ri.hasGateway()) continue; - - final String gateway = ri.getGateway().getHostAddress(); - final InetAddress address = ri.getDestination().getAddress(); - if (ri.isDefaultRoute() && address instanceof Inet4Address) { - v4gateway = gateway; - } else if (ri.isDefaultRoute() && address instanceof Inet6Address) { - v6gateways.add(gateway); - } - } - - boolean success = mHwInterface.setUpstreamParameters( - iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); - - if (!success) { - return success; - } - - // Update stats after we've told the hardware to change routing so we don't miss packets. - maybeUpdateStats(prevUpstream); - - // Data limits can only be set once offload is running on the upstream. - success = maybeUpdateDataLimit(iface); - if (!success) { - // If we failed to set a data limit, don't use this upstream, because we don't want to - // blow through the data limit that we were told to apply. - mLog.log("Setting data limit for " + iface + " failed, disabling offload."); - stop(); - } - - return success; - } - - private boolean computeAndPushLocalPrefixes(UpdateType how) { - final boolean force = (how == UpdateType.FORCE); - final Set<String> localPrefixStrs = computeLocalPrefixStrings( - mExemptPrefixes, mUpstreamLinkProperties); - if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; - - mLastLocalPrefixStrs = localPrefixStrs; - return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); - } - - // TODO: Factor in downstream LinkProperties once that information is available. - private static Set<String> computeLocalPrefixStrings( - Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) { - // Create an editable copy. - final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes); - - // TODO: If a downstream interface (not currently passed in) is reusing - // the /64 of the upstream (64share) then: - // - // [a] remove that /64 from the local prefixes - // [b] add in /128s for IP addresses on the downstream interface - // [c] add in /128s for IP addresses on the upstream interface - // - // Until downstream information is available here, simply add /128s from - // the upstream network; they'll just be redundant with their /64. - if (upstreamLinkProperties != null) { - for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { - if (!linkAddr.isGlobalPreferred()) continue; - final InetAddress ip = linkAddr.getAddress(); - if (!(ip instanceof Inet6Address)) continue; - prefixSet.add(new IpPrefix(ip, 128)); - } - } - - final HashSet<String> localPrefixStrs = new HashSet<>(); - for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); - return localPrefixStrs; - } - - private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) { - // Ignore any link-local routes. - final IpPrefix destination = route.getDestination(); - final LinkAddress linkAddr = new LinkAddress(destination.getAddress(), - destination.getPrefixLength()); - if (!linkAddr.isGlobalPreferred()) return true; - - return false; - } - - /** Dump information. */ - public void dump(IndentingPrintWriter pw) { - if (isOffloadDisabled()) { - pw.println("Offload disabled"); - return; - } - final boolean isStarted = started(); - pw.println("Offload HALs " + (isStarted ? "started" : "not started")); - LinkProperties lp = mUpstreamLinkProperties; - String upstream = (lp != null) ? lp.getInterfaceName() : null; - pw.println("Current upstream: " + upstream); - pw.println("Exempt prefixes: " + mLastLocalPrefixStrs); - pw.println("NAT timeout update callbacks received during the " - + (isStarted ? "current" : "last") - + " offload session: " - + mNatUpdateCallbacksReceived); - pw.println("NAT timeout update netlink errors during the " - + (isStarted ? "current" : "last") - + " offload session: " - + mNatUpdateNetlinkErrors); - } - - private void updateNatTimeout( - int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) { - final String protoName = protoNameFor(proto); - if (protoName == null) { - mLog.e("Unknown NAT update callback protocol: " + proto); - return; - } - - final Inet4Address src = parseIPv4Address(srcAddr); - if (src == null) { - mLog.e("Failed to parse IPv4 address: " + srcAddr); - return; - } - - if (!isValidUdpOrTcpPort(srcPort)) { - mLog.e("Invalid src port: " + srcPort); - return; - } - - final Inet4Address dst = parseIPv4Address(dstAddr); - if (dst == null) { - mLog.e("Failed to parse IPv4 address: " + dstAddr); - return; - } - - if (!isValidUdpOrTcpPort(dstPort)) { - mLog.e("Invalid dst port: " + dstPort); - return; - } - - mNatUpdateCallbacksReceived++; - final String natDescription = String.format("%s (%s, %s) -> (%s, %s)", - protoName, srcAddr, srcPort, dstAddr, dstPort); - if (DBG) { - mLog.log("NAT timeout update: " + natDescription); - } - - final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); - final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest( - proto, src, srcPort, dst, dstPort, timeoutSec); - - try { - NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); - } catch (ErrnoException e) { - mNatUpdateNetlinkErrors++; - mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e - + ", msg: " + NetlinkConstants.hexify(msg)); - mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); - mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); - } - } - - private static Inet4Address parseIPv4Address(String addrString) { - try { - final InetAddress ip = InetAddresses.parseNumericAddress(addrString); - // TODO: Consider other sanitization steps here, including perhaps: - // not eql to 0.0.0.0 - // not within 169.254.0.0/16 - // not within ::ffff:0.0.0.0/96 - // not within ::/96 - // et cetera. - if (ip instanceof Inet4Address) { - return (Inet4Address) ip; - } - } catch (IllegalArgumentException iae) { } - return null; - } - - private static String protoNameFor(int proto) { - // OsConstants values are not constant expressions; no switch statement. - if (proto == OsConstants.IPPROTO_UDP) { - return "UDP"; - } else if (proto == OsConstants.IPPROTO_TCP) { - return "TCP"; - } - return null; - } - - private static int connectionTimeoutUpdateSecondsFor(int proto) { - // TODO: Replace this with more thoughtful work, perhaps reading from - // and maybe writing to any required - // - // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_* - // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream} - // - // entries. TBD. - if (proto == OsConstants.IPPROTO_TCP) { - // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established - return 432000; - } else { - // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream - return 180; - } - } - - private static boolean isValidUdpOrTcpPort(int port) { - return port > 0 && port < 65536; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java deleted file mode 100644 index da5f25b2a596..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; -import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; -import static android.net.util.TetheringUtils.uint16; - -import android.annotation.NonNull; -import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; -import android.hardware.tetheroffload.control.V1_0.IOffloadControl; -import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; -import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; -import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; -import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; -import android.net.netlink.NetlinkSocket; -import android.net.netlink.StructNfGenMsg; -import android.net.netlink.StructNlMsgHdr; -import android.net.util.SharedLog; -import android.net.util.SocketUtils; -import android.os.Handler; -import android.os.NativeHandle; -import android.os.RemoteException; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.NoSuchElementException; - - -/** - * Capture tethering dependencies, for injection. - * - * @hide - */ -public class OffloadHardwareInterface { - private static final String TAG = OffloadHardwareInterface.class.getSimpleName(); - private static final String YIELDS = " -> "; - // Change this value to control whether tether offload is enabled or - // disabled by default in the absence of an explicit Settings value. - // See accompanying unittest to distinguish 0 from non-0 values. - private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0; - private static final String NO_INTERFACE_NAME = ""; - private static final String NO_IPV4_ADDRESS = ""; - private static final String NO_IPV4_GATEWAY = ""; - // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h - public static final int NF_NETLINK_CONNTRACK_NEW = 1; - public static final int NF_NETLINK_CONNTRACK_UPDATE = 2; - public static final int NF_NETLINK_CONNTRACK_DESTROY = 4; - // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h - public static final short NFNL_SUBSYS_CTNETLINK = 1; - public static final short IPCTNL_MSG_CT_NEW = 0; - public static final short IPCTNL_MSG_CT_GET = 1; - - private final long NETLINK_MESSAGE_TIMEOUT_MS = 500; - - private final Handler mHandler; - private final SharedLog mLog; - private final Dependencies mDeps; - private IOffloadControl mOffloadControl; - private TetheringOffloadCallback mTetheringOffloadCallback; - private ControlCallback mControlCallback; - - /** The callback to notify status of offload management process. */ - public static class ControlCallback { - /** Offload started. */ - public void onStarted() {} - /** - * Offload stopped because an error has occurred in lower layer. - */ - public void onStoppedError() {} - /** - * Offload stopped because the device has moved to a bearer on which hardware offload is - * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will - * likely fail and cannot be presumed to be saved inside of the hardware management process. - * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin - * offload again. - */ - public void onStoppedUnsupported() {} - /** Indicate that offload is able to proivde support for this time. */ - public void onSupportAvailable() {} - /** Offload stopped because of usage limit reached. */ - public void onStoppedLimitReached() {} - - /** Indicate to update NAT timeout. */ - public void onNatTimeoutUpdate(int proto, - String srcAddr, int srcPort, - String dstAddr, int dstPort) {} - } - - /** The object which records Tx/Rx forwarded bytes. */ - public static class ForwardedStats { - public long rxBytes; - public long txBytes; - - public ForwardedStats() { - rxBytes = 0; - txBytes = 0; - } - - @VisibleForTesting - public ForwardedStats(long rxBytes, long txBytes) { - this.rxBytes = rxBytes; - this.txBytes = txBytes; - } - - /** Add Tx/Rx bytes. */ - public void add(ForwardedStats other) { - rxBytes += other.rxBytes; - txBytes += other.txBytes; - } - - /** Returns the string representation of this object. */ - public String toString() { - return String.format("rx:%s tx:%s", rxBytes, txBytes); - } - } - - public OffloadHardwareInterface(Handler h, SharedLog log) { - this(h, log, new Dependencies(log)); - } - - OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) { - mHandler = h; - mLog = log.forSubComponent(TAG); - mDeps = deps; - } - - /** Capture OffloadHardwareInterface dependencies, for injection. */ - static class Dependencies { - private final SharedLog mLog; - - Dependencies(SharedLog log) { - mLog = log; - } - - public IOffloadConfig getOffloadConfig() { - try { - return IOffloadConfig.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("getIOffloadConfig error " + e); - return null; - } - } - - public IOffloadControl getOffloadControl() { - try { - return IOffloadControl.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("tethering offload control not supported: " + e); - return null; - } - } - - public NativeHandle createConntrackSocket(final int groups) { - final FileDescriptor fd; - try { - fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER); - } catch (ErrnoException e) { - mLog.e("Unable to create conntrack socket " + e); - return null; - } - - final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); - try { - Os.bind(fd, sockAddr); - } catch (ErrnoException | SocketException e) { - mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); - try { - SocketUtils.closeSocket(fd); - } catch (IOException ie) { - // Nothing we can do here - } - return null; - } - try { - Os.connect(fd, sockAddr); - } catch (ErrnoException | SocketException e) { - mLog.e("connect to kernel fail for groups " + groups + " error: " + e); - try { - SocketUtils.closeSocket(fd); - } catch (IOException ie) { - // Nothing we can do here - } - return null; - } - - return new NativeHandle(fd, true); - } - } - - /** Get default value indicating whether offload is supported. */ - public int getDefaultTetherOffloadDisabled() { - return DEFAULT_TETHER_OFFLOAD_DISABLED; - } - - /** - * Offload management process need to know conntrack rules to support NAT, but it may not have - * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and - * share them with offload management process. - */ - public boolean initOffloadConfig() { - final IOffloadConfig offloadConfig = mDeps.getOffloadConfig(); - if (offloadConfig == null) { - mLog.e("Could not find IOffloadConfig service"); - return false; - } - // Per the IConfigOffload definition: - // - // h1 provides a file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). - // - // h2 provides a file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). - final NativeHandle h1 = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); - if (h1 == null) return false; - - sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), - (short) (NLM_F_REQUEST | NLM_F_DUMP)); - - final NativeHandle h2 = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); - if (h2 == null) { - closeFdInNativeHandle(h1); - return false; - } - - final CbResults results = new CbResults(); - try { - offloadConfig.setHandles(h1, h2, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record("initOffloadConfig, setHandles fail", e); - return false; - } - // Explicitly close FDs. - closeFdInNativeHandle(h1); - closeFdInNativeHandle(h2); - - record("initOffloadConfig, setHandles results:", results); - return results.mSuccess; - } - - @VisibleForTesting - public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) { - final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; - final byte[] msg = new byte[length]; - final ByteBuffer byteBuffer = ByteBuffer.wrap(msg); - byteBuffer.order(ByteOrder.nativeOrder()); - - final StructNlMsgHdr nlh = new StructNlMsgHdr(); - nlh.nlmsg_len = length; - nlh.nlmsg_type = type; - nlh.nlmsg_flags = flags; - nlh.nlmsg_seq = 0; - nlh.pack(byteBuffer); - - // Header needs to be added to buffer since a generic netlink request is being sent. - final StructNfGenMsg nfh = new StructNfGenMsg((byte) OsConstants.AF_INET); - nfh.pack(byteBuffer); - - try { - NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length, - NETLINK_MESSAGE_TIMEOUT_MS); - } catch (ErrnoException | InterruptedIOException e) { - mLog.e("Unable to send netfilter message, error: " + e); - } - } - - private void closeFdInNativeHandle(final NativeHandle h) { - try { - h.close(); - } catch (IOException | IllegalStateException e) { - // IllegalStateException means fd is already closed, do nothing here. - // Also nothing we can do if IOException. - } - } - - /** Initialize the tethering offload HAL. */ - public boolean initOffloadControl(ControlCallback controlCb) { - mControlCallback = controlCb; - - if (mOffloadControl == null) { - mOffloadControl = mDeps.getOffloadControl(); - if (mOffloadControl == null) { - mLog.e("tethering IOffloadControl.getService() returned null"); - return false; - } - } - - final String logmsg = String.format("initOffloadControl(%s)", - (controlCb == null) ? "null" - : "0x" + Integer.toHexString(System.identityHashCode(controlCb))); - - mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog); - final CbResults results = new CbResults(); - try { - mOffloadControl.initOffload( - mTetheringOffloadCallback, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Stop IOffloadControl. */ - public void stopOffloadControl() { - if (mOffloadControl != null) { - try { - mOffloadControl.stopOffload( - (boolean success, String errMsg) -> { - if (!success) mLog.e("stopOffload failed: " + errMsg); - }); - } catch (RemoteException e) { - mLog.e("failed to stopOffload: " + e); - } - } - mOffloadControl = null; - mTetheringOffloadCallback = null; - mControlCallback = null; - mLog.log("stopOffloadControl()"); - } - - /** Get Tx/Rx usage from last query. */ - public ForwardedStats getForwardedStats(String upstream) { - final String logmsg = String.format("getForwardedStats(%s)", upstream); - - final ForwardedStats stats = new ForwardedStats(); - try { - mOffloadControl.getForwardedStats( - upstream, - (long rxBytes, long txBytes) -> { - stats.rxBytes = (rxBytes > 0) ? rxBytes : 0; - stats.txBytes = (txBytes > 0) ? txBytes : 0; - }); - } catch (RemoteException e) { - record(logmsg, e); - return stats; - } - - return stats; - } - - /** Set local prefixes to offload management process. */ - public boolean setLocalPrefixes(ArrayList<String> localPrefixes) { - final String logmsg = String.format("setLocalPrefixes([%s])", - String.join(",", localPrefixes)); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setLocalPrefixes(localPrefixes, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Set data limit value to offload management process. */ - public boolean setDataLimit(String iface, long limit) { - - final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setDataLimit( - iface, limit, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Set upstream parameters to offload management process. */ - public boolean setUpstreamParameters( - String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { - iface = (iface != null) ? iface : NO_INTERFACE_NAME; - v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS; - v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY; - v6gws = (v6gws != null) ? v6gws : new ArrayList<>(); - - final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])", - iface, v4addr, v4gateway, String.join(",", v6gws)); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setUpstreamParameters( - iface, v4addr, v4gateway, v6gws, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Add downstream prefix to offload management process. */ - public boolean addDownstreamPrefix(String ifname, String prefix) { - final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix); - - final CbResults results = new CbResults(); - try { - mOffloadControl.addDownstream(ifname, prefix, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - /** Remove downstream prefix from offload management process. */ - public boolean removeDownstreamPrefix(String ifname, String prefix) { - final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix); - - final CbResults results = new CbResults(); - try { - mOffloadControl.removeDownstream(ifname, prefix, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - private void record(String msg, Throwable t) { - mLog.e(msg + YIELDS + "exception: " + t); - } - - private void record(String msg, CbResults results) { - final String logmsg = msg + YIELDS + results; - if (!results.mSuccess) { - mLog.e(logmsg); - } else { - mLog.log(logmsg); - } - } - - private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { - public final Handler handler; - public final ControlCallback controlCb; - public final SharedLog log; - - TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) { - handler = h; - controlCb = cb; - log = sharedLog; - } - - @Override - public void onEvent(int event) { - handler.post(() -> { - switch (event) { - case OffloadCallbackEvent.OFFLOAD_STARTED: - controlCb.onStarted(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: - controlCb.onStoppedError(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: - controlCb.onStoppedUnsupported(); - break; - case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: - controlCb.onSupportAvailable(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: - controlCb.onStoppedLimitReached(); - break; - default: - log.e("Unsupported OffloadCallbackEvent: " + event); - } - }); - } - - @Override - public void updateTimeout(NatTimeoutUpdate params) { - handler.post(() -> { - controlCb.onNatTimeoutUpdate( - networkProtocolToOsConstant(params.proto), - params.src.addr, uint16(params.src.port), - params.dst.addr, uint16(params.dst.port)); - }); - } - } - - private static int networkProtocolToOsConstant(int proto) { - switch (proto) { - case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; - case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; - default: - // The caller checks this value and will log an error. Just make - // sure it won't collide with valid OsContants.IPPROTO_* values. - return -Math.abs(proto); - } - } - - private static class CbResults { - boolean mSuccess; - String mErrMsg; - - @Override - public String toString() { - if (mSuccess) { - return "ok"; - } else { - return "fail: " + mErrMsg; - } - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java deleted file mode 100644 index 4f616cdff086..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.util.PrefixUtils.asIpPrefix; - -import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH; -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; -import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH; - -import static java.util.Arrays.asList; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.Network; -import android.net.ip.IpServer; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; - -/** - * This class coordinate IP addresses conflict problem. - * - * Tethering downstream IP addresses may conflict with network assigned addresses. This - * coordinator is responsible for recording all of network assigned addresses and dispatched - * free address to downstream interfaces. - * - * This class is not thread-safe and should be accessed on the same tethering internal thread. - * @hide - */ -public class PrivateAddressCoordinator { - public static final int PREFIX_LENGTH = 24; - - // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream - // address may be requested before coordinator get current upstream notification. To ensure - // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared - // when tethering is down. Instead tethering would remove all deprecated upstreams from - // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams(). - private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap; - private final ArraySet<IpServer> mDownstreams; - private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24"; - private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24"; - private final List<IpPrefix> mTetheringPrefixes; - private final ConnectivityManager mConnectivityMgr; - private final TetheringConfiguration mConfig; - // keyed by downstream type(TetheringManager.TETHERING_*). - private final SparseArray<LinkAddress> mCachedAddresses; - - public PrivateAddressCoordinator(Context context, TetheringConfiguration config) { - mDownstreams = new ArraySet<>(); - mUpstreamPrefixMap = new ArrayMap<>(); - mConnectivityMgr = (ConnectivityManager) context.getSystemService( - Context.CONNECTIVITY_SERVICE); - mConfig = config; - mCachedAddresses = new SparseArray<>(); - // Reserved static addresses for bluetooth and wifi p2p. - mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS)); - mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS)); - - mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))); - if (config.isSelectAllPrefixRangeEnabled()) { - mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12")); - mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8")); - } - } - - /** - * Record a new upstream IpPrefix which may conflict with tethering downstreams. - * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called, - * UpstreamNetworkState must have an already populated LinkProperties. - */ - public void updateUpstreamPrefix(final UpstreamNetworkState ns) { - // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null, - // but just checking to be sure. - if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) { - removeUpstreamPrefix(ns.network); - return; - } - - final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes( - ns.linkProperties.getAllLinkAddresses()); - if (ipv4Prefixes.isEmpty()) { - removeUpstreamPrefix(ns.network); - return; - } - - mUpstreamPrefixMap.put(ns.network, ipv4Prefixes); - handleMaybePrefixConflict(ipv4Prefixes); - } - - private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) { - final ArrayList<IpPrefix> list = new ArrayList<>(); - for (LinkAddress address : linkAddresses) { - if (!address.isIpv4()) continue; - - list.add(asIpPrefix(address)); - } - - return list; - } - - private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) { - for (IpServer downstream : mDownstreams) { - final IpPrefix target = getDownstreamPrefix(downstream); - - for (IpPrefix source : prefixes) { - if (isConflictPrefix(source, target)) { - downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - break; - } - } - } - } - - /** Remove IpPrefix records corresponding to input network. */ - public void removeUpstreamPrefix(final Network network) { - mUpstreamPrefixMap.remove(network); - } - - /** - * Maybe remove deprecated upstream records, this would be called once tethering started without - * any exiting tethered downstream. - */ - public void maybeRemoveDeprecatedUpstreams() { - if (mUpstreamPrefixMap.isEmpty()) return; - - // Remove all upstreams that are no longer valid networks - final Set<Network> toBeRemoved = new HashSet<>(mUpstreamPrefixMap.keySet()); - toBeRemoved.removeAll(asList(mConnectivityMgr.getAllNetworks())); - - mUpstreamPrefixMap.removeAll(toBeRemoved); - } - - /** - * Pick a random available address and mark its prefix as in use for the provided IpServer, - * returns null if there is no available address. - */ - @Nullable - public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) { - if (mConfig.shouldEnableWifiP2pDedicatedIp() - && ipServer.interfaceType() == TETHERING_WIFI_P2P) { - return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS); - } - - final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType()); - if (useLastAddress && cachedAddress != null - && !isConflictWithUpstream(asIpPrefix(cachedAddress))) { - mDownstreams.add(ipServer); - return cachedAddress; - } - - for (IpPrefix prefixRange : mTetheringPrefixes) { - final LinkAddress newAddress = chooseDownstreamAddress(prefixRange); - if (newAddress != null) { - mDownstreams.add(ipServer); - mCachedAddresses.put(ipServer.interfaceType(), newAddress); - return newAddress; - } - } - - // No available address. - return null; - } - - private int getPrefixBaseAddress(final IpPrefix prefix) { - return inet4AddressToIntHTH((Inet4Address) prefix.getAddress()); - } - - /** - * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes. - * If yes, return one of them. - */ - private IpPrefix getConflictPrefix(final IpPrefix prefix) { - final IpPrefix upstream = getConflictWithUpstream(prefix); - if (upstream != null) return upstream; - - return getInUseDownstreamPrefix(prefix); - } - - // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the - // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix - // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is - // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0). - // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as - // selected random sub address later. - private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) { - final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength()); - // The largest offset within the prefix assignment block that still conflicts with - // conflictPrefix. - final int maxConflict = - (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask; - - final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH); - // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than - // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix. - // There is no need to mask the result with PREFIX_LENGTH bits because this is done by - // findAvailablePrefixFromRange when it constructs the prefix. - return maxConflict + (1 << (32 - PREFIX_LENGTH)); - } - - private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) { - // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12). - final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength()); - - // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12). - final int baseAddress = getPrefixBaseAddress(prefixRange); - - // The subnet mask corresponding to PREFIX_LENGTH. - final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH); - - // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH. - // This may not be the prefix of the address returned by this method: - // - If it is already in use, the method will return an address in another prefix. - // - If all prefixes within prefixRange are in use, the method will return null. For - // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in - // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00. - // - // prefixRangeMask is required to ensure no wrapping. For example, consider: - // - prefixRange 127.0.0.0/8 - // - randomPrefixStart 127.255.255.0 - // - A conflicting prefix of 127.255.254.0/23 - // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which - // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix - // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648 - // is less than 127.0.0.0 = 0x7f000000 = 2130706432. - // - // Additionally, it makes debug output easier to read by making the numbers smaller. - final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask; - - // A random offset within the prefix. Used to determine the local address once the prefix - // is selected. It does not result in an IPv4 address ending in .0, .1, or .255 - // For a PREFIX_LENGTH of 255, this is a number between 2 and 254. - final int subAddress = getSanitizedSubAddr(~prefixMask); - - // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block, - // such that the prefix does not conflict with any upstream. - IpPrefix downstreamPrefix = findAvailablePrefixFromRange( - randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask); - if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress); - - // If that failed, do the same, but between 0 and randomPrefixStart. - downstreamPrefix = findAvailablePrefixFromRange( - 0, randomPrefixStart, baseAddress, prefixRangeMask); - - return getLinkAddress(downstreamPrefix, subAddress); - } - - private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) { - if (prefix == null) return null; - - final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress); - return new LinkAddress(address, PREFIX_LENGTH); - } - - private IpPrefix findAvailablePrefixFromRange(final int start, final int end, - final int baseAddress, final int prefixRangeMask) { - int newSubPrefix = start; - while (newSubPrefix < end) { - final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix); - final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH); - - final IpPrefix conflictPrefix = getConflictPrefix(prefix); - - if (conflictPrefix == null) return prefix; - - newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask); - } - - return null; - } - - /** Get random int which could be used to generate random address. */ - @VisibleForTesting - public int getRandomInt() { - return (new Random()).nextInt(); - } - - /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */ - private int getSanitizedSubAddr(final int subAddrMask) { - final int randomSubAddr = getRandomInt() & subAddrMask; - // If prefix length > 30, the selecting speace would be less than 4 which may be hard to - // avoid 3 consecutive address. - if (PREFIX_LENGTH > 30) return randomSubAddr; - - // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering - // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer - // than 24 - final int candidate = randomSubAddr & 0xff; - if (candidate == 0 || candidate == 1 || candidate == 255) { - return (randomSubAddr & 0xfffffffc) + 2; - } - - return randomSubAddr; - } - - /** Release downstream record for IpServer. */ - public void releaseDownstream(final IpServer ipServer) { - mDownstreams.remove(ipServer); - } - - /** Clear current upstream prefixes records. */ - public void clearUpstreamPrefixes() { - mUpstreamPrefixMap.clear(); - } - - private IpPrefix getConflictWithUpstream(final IpPrefix prefix) { - for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { - final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i); - for (IpPrefix upstream : list) { - if (isConflictPrefix(prefix, upstream)) return upstream; - } - } - return null; - } - - private boolean isConflictWithUpstream(final IpPrefix prefix) { - return getConflictWithUpstream(prefix) != null; - } - - private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) { - if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) { - return prefix2.contains(prefix1.getAddress()); - } - - return prefix1.contains(prefix2.getAddress()); - } - - // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last - // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p). - private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) { - for (int i = 0; i < mCachedAddresses.size(); i++) { - final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i)); - if (isConflictPrefix(prefix, downstream)) return downstream; - } - - // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include - // in mCachedAddresses. - for (IpServer downstream : mDownstreams) { - final IpPrefix target = getDownstreamPrefix(downstream); - - if (isConflictPrefix(prefix, target)) return target; - } - - return null; - } - - @NonNull - private IpPrefix getDownstreamPrefix(final IpServer downstream) { - final LinkAddress address = downstream.getAddress(); - - return asIpPrefix(address); - } - - void dump(final IndentingPrintWriter pw) { - pw.println("mTetheringPrefixes:"); - pw.increaseIndent(); - for (IpPrefix prefix : mTetheringPrefixes) { - pw.println(prefix); - } - pw.decreaseIndent(); - - pw.println("mUpstreamPrefixMap:"); - pw.increaseIndent(); - for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { - pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i)); - } - pw.decreaseIndent(); - - pw.println("mDownstreams:"); - pw.increaseIndent(); - for (IpServer ipServer : mDownstreams) { - pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress()); - } - pw.decreaseIndent(); - - pw.println("mCachedAddresses:"); - pw.increaseIndent(); - for (int i = 0; i < mCachedAddresses.size(); i++) { - pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i)); - } - pw.decreaseIndent(); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java deleted file mode 100644 index 5a0c5b0cff5f..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ /dev/null @@ -1,2427 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.PackageManager.GET_ACTIVITIES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.hardware.usb.UsbManager.USB_CONFIGURED; -import static android.hardware.usb.UsbManager.USB_CONNECTED; -import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; -import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; -import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; -import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; -import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; -import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; -import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; -import static android.net.TetheringManager.TETHERING_NCM; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHERING_WIGIG; -import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; -import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; -import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; -import static android.net.util.TetheringMessageBase.BASE_MAIN_SM; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; -import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; - -import android.app.usage.NetworkStatsManager; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothPan; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.hardware.usb.UsbManager; -import android.net.ConnectivityManager; -import android.net.EthernetManager; -import android.net.IIntResultListener; -import android.net.INetd; -import android.net.ITetheringEventCallback; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.TetherStatesParcel; -import android.net.TetheredClient; -import android.net.TetheringCallbackStartedParcel; -import android.net.TetheringConfigurationParcel; -import android.net.TetheringRequestParcel; -import android.net.ip.IpServer; -import android.net.shared.NetdUtils; -import android.net.util.BaseNetdUnsolicitedEventListener; -import android.net.util.InterfaceSet; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.net.util.TetheringUtils; -import android.net.util.VersionedBroadcastListener; -import android.net.wifi.WifiClient; -import android.net.wifi.WifiManager; -import android.net.wifi.p2p.WifiP2pGroup; -import android.net.wifi.p2p.WifiP2pInfo; -import android.net.wifi.p2p.WifiP2pManager; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.ServiceSpecificException; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.MessageUtils; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.RejectedExecutionException; - -/** - * - * This class holds much of the business logic to allow Android devices - * to act as IP gateways via USB, BT, and WiFi interfaces. - */ -public class Tethering { - - private static final String TAG = Tethering.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - private static final Class[] sMessageClasses = { - Tethering.class, TetherMainSM.class, IpServer.class - }; - private static final SparseArray<String> sMagicDecoderRing = - MessageUtils.findMessageNames(sMessageClasses); - // Keep in sync with NETID_UNSET in system/netd/include/netid_client.h - private static final int NETID_UNSET = 0; - - private static class TetherState { - public final IpServer ipServer; - public int lastState; - public int lastError; - - TetherState(IpServer ipServer) { - this.ipServer = ipServer; - // Assume all state machines start out available and with no errors. - lastState = IpServer.STATE_AVAILABLE; - lastError = TETHER_ERROR_NO_ERROR; - } - - public boolean isCurrentlyServing() { - switch (lastState) { - case IpServer.STATE_TETHERED: - case IpServer.STATE_LOCAL_ONLY: - return true; - default: - return false; - } - } - } - - /** - * Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}. - */ - private static class CallbackCookie { - public final boolean hasListClientsPermission; - - private CallbackCookie(boolean hasListClientsPermission) { - this.hasListClientsPermission = hasListClientsPermission; - } - } - - private final SharedLog mLog = new SharedLog(TAG); - private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks = - new RemoteCallbackList<>(); - // Currently active tethering requests per tethering type. Only one of each type can be - // requested at a time. After a tethering type is requested, the map keeps tethering parameters - // to be used after the interface comes up asynchronously. - private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests = - new SparseArray<>(); - - // used to synchronize public access to members - // TODO(b/153621704): remove mPublicSync to make Tethering lock free - private final Object mPublicSync; - private final Context mContext; - private final ArrayMap<String, TetherState> mTetherStates; - private final BroadcastReceiver mStateReceiver; - private final Looper mLooper; - private final StateMachine mTetherMainSM; - private final OffloadController mOffloadController; - private final UpstreamNetworkMonitor mUpstreamNetworkMonitor; - // TODO: Figure out how to merge this and other downstream-tracking objects - // into a single coherent structure. - // Use LinkedHashSet for predictable ordering order for ConnectedClientsTracker. - private final LinkedHashSet<IpServer> mForwardedDownstreams; - private final VersionedBroadcastListener mCarrierConfigChange; - private final TetheringDependencies mDeps; - private final EntitlementManager mEntitlementMgr; - private final Handler mHandler; - private final INetd mNetd; - private final NetdCallback mNetdCallback; - private final UserRestrictionActionListener mTetheringRestriction; - private final ActiveDataSubIdListener mActiveDataSubIdListener; - private final ConnectedClientsTracker mConnectedClientsTracker; - private final TetheringThreadExecutor mExecutor; - private final TetheringNotificationUpdater mNotificationUpdater; - private final UserManager mUserManager; - private final BpfCoordinator mBpfCoordinator; - private final PrivateAddressCoordinator mPrivateAddressCoordinator; - private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; - // All the usage of mTetheringEventCallback should run in the same thread. - private ITetheringEventCallback mTetheringEventCallback = null; - - private volatile TetheringConfiguration mConfig; - private InterfaceSet mCurrentUpstreamIfaceSet; - - private boolean mRndisEnabled; // track the RNDIS function enabled state - // True iff. WiFi tethering should be started when soft AP is ready. - private boolean mWifiTetherRequested; - private Network mTetherUpstream; - private TetherStatesParcel mTetherStatesParcel; - private boolean mDataSaverEnabled = false; - private String mWifiP2pTetherInterface = null; - private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED; - - @GuardedBy("mPublicSync") - private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest; - @GuardedBy("mPublicSync") - private String mConfiguredEthernetIface; - @GuardedBy("mPublicSync") - private EthernetCallback mEthernetCallback; - - public Tethering(TetheringDependencies deps) { - mLog.mark("Tethering.constructed"); - mDeps = deps; - mContext = mDeps.getContext(); - mNetd = mDeps.getINetd(mContext); - mLooper = mDeps.getTetheringLooper(); - mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper); - - mPublicSync = new Object(); - - mTetherStates = new ArrayMap<>(); - mConnectedClientsTracker = new ConnectedClientsTracker(); - - mTetherMainSM = new TetherMainSM("TetherMain", mLooper, deps); - mTetherMainSM.start(); - - mHandler = mTetherMainSM.getHandler(); - mOffloadController = mDeps.getOffloadController(mHandler, mLog, - new OffloadController.Dependencies() { - - @Override - public TetheringConfiguration getTetherConfig() { - return mConfig; - } - }); - mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMainSM, mLog, - TetherMainSM.EVENT_UPSTREAM_CALLBACK); - mForwardedDownstreams = new LinkedHashSet<>(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); - // EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream - // permission is changed according to entitlement check result. - mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog, - () -> mTetherMainSM.sendMessage( - TetherMainSM.EVENT_UPSTREAM_PERMISSION_CHANGED)); - mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> { - mLog.log("OBSERVED UiEnitlementFailed"); - stopTethering(downstream); - }); - mEntitlementMgr.setTetheringConfigurationFetcher(() -> { - return mConfig; - }); - - mCarrierConfigChange = new VersionedBroadcastListener( - "CarrierConfigChangeListener", mContext, mHandler, filter, - (Intent ignored) -> { - mLog.log("OBSERVED carrier config change"); - updateConfiguration(); - mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); - }); - - mStateReceiver = new StateReceiver(); - - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mTetheringRestriction = new UserRestrictionActionListener( - mUserManager, this, mNotificationUpdater); - mExecutor = new TetheringThreadExecutor(mHandler); - mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); - mNetdCallback = new NetdCallback(); - - // Load tethering configuration. - updateConfiguration(); - // It is OK for the configuration to be passed to the PrivateAddressCoordinator at - // construction time because the only part of the configuration it uses is - // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that. - mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig); - - // Must be initialized after tethering configuration is loaded because BpfCoordinator - // constructor needs to use the configuration. - mBpfCoordinator = mDeps.getBpfCoordinator( - new BpfCoordinator.Dependencies() { - @NonNull - public Handler getHandler() { - return mHandler; - } - - @NonNull - public INetd getNetd() { - return mNetd; - } - - @NonNull - public NetworkStatsManager getNetworkStatsManager() { - return mContext.getSystemService(NetworkStatsManager.class); - } - - @NonNull - public SharedLog getSharedLog() { - return mLog; - } - - @Nullable - public TetheringConfiguration getTetherConfig() { - return mConfig; - } - }); - - startStateMachineUpdaters(); - } - - /** - * Start to register callbacks. - * Call this function when tethering is ready to handle callback events. - */ - private void startStateMachineUpdaters() { - try { - mNetd.registerUnsolicitedEventListener(mNetdCallback); - } catch (RemoteException e) { - mLog.e("Unable to register netd UnsolicitedEventListener"); - } - mCarrierConfigChange.startListening(); - mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener, - PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); - - IntentFilter filter = new IntentFilter(); - filter.addAction(UsbManager.ACTION_USB_STATE); - filter.addAction(CONNECTIVITY_ACTION); - filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); - filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, mHandler); - - final IntentFilter noUpstreamFilter = new IntentFilter(); - noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING); - mContext.registerReceiver( - mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler); - - final WifiManager wifiManager = getWifiManager(); - if (wifiManager != null) { - wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); - } - - startTrackDefaultNetwork(); - } - - private class TetheringThreadExecutor implements Executor { - private final Handler mTetherHandler; - TetheringThreadExecutor(Handler handler) { - mTetherHandler = handler; - } - @Override - public void execute(Runnable command) { - if (!mTetherHandler.post(command)) { - throw new RejectedExecutionException(mTetherHandler + " is shutting down"); - } - } - } - - private class ActiveDataSubIdListener extends PhoneStateListener { - ActiveDataSubIdListener(Executor executor) { - super(executor); - } - - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId - + " to " + subId); - if (subId == mActiveDataSubId) return; - - mActiveDataSubId = subId; - updateConfiguration(); - mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId); - // To avoid launching unexpected provisioning checks, ignore re-provisioning - // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() - // will be triggered again when CarrierConfig is loaded. - if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { - mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); - } else { - mLog.log("IGNORED reevaluate provisioning, no carrier config loaded"); - } - } - } - - private WifiManager getWifiManager() { - return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - } - - // NOTE: This is always invoked on the mLooper thread. - private void updateConfiguration() { - mConfig = mDeps.generateTetheringConfiguration(mContext, mLog, mActiveDataSubId); - mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); - reportConfigurationChanged(mConfig.toStableParcelable()); - } - - private void maybeDunSettingChanged() { - final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext); - if (isDunRequired == mConfig.isDunRequired) return; - updateConfiguration(); - } - - private class NetdCallback extends BaseNetdUnsolicitedEventListener { - @Override - public void onInterfaceChanged(String ifName, boolean up) { - mHandler.post(() -> interfaceStatusChanged(ifName, up)); - } - - @Override - public void onInterfaceLinkStateChanged(String ifName, boolean up) { - mHandler.post(() -> interfaceLinkStateChanged(ifName, up)); - } - - @Override - public void onInterfaceAdded(String ifName) { - mHandler.post(() -> interfaceAdded(ifName)); - } - - @Override - public void onInterfaceRemoved(String ifName) { - mHandler.post(() -> interfaceRemoved(ifName)); - } - } - - private class TetheringSoftApCallback implements WifiManager.SoftApCallback { - // TODO: Remove onStateChanged override when this method has default on - // WifiManager#SoftApCallback interface. - // Wifi listener for state change of the soft AP - @Override - public void onStateChanged(final int state, final int failureReason) { - // Nothing - } - - // Called by wifi when the number of soft AP clients changed. - @Override - public void onConnectedClientsChanged(final List<WifiClient> clients) { - updateConnectedClients(clients); - } - } - - void interfaceStatusChanged(String iface, boolean up) { - // Never called directly: only called from interfaceLinkStateChanged. - // See NetlinkHandler.cpp: notifyInterfaceChanged. - if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up); - synchronized (mPublicSync) { - if (up) { - maybeTrackNewInterfaceLocked(iface); - } else { - if (ifaceNameToType(iface) == TETHERING_BLUETOOTH - || ifaceNameToType(iface) == TETHERING_WIGIG) { - stopTrackingInterfaceLocked(iface); - } else { - // Ignore usb0 down after enabling RNDIS. - // We will handle disconnect in interfaceRemoved. - // Similarly, ignore interface down for WiFi. We monitor WiFi AP status - // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent. - if (VDBG) Log.d(TAG, "ignore interface down for " + iface); - } - } - } - } - - void interfaceLinkStateChanged(String iface, boolean up) { - interfaceStatusChanged(iface, up); - } - - private int ifaceNameToType(String iface) { - final TetheringConfiguration cfg = mConfig; - - if (cfg.isWifi(iface)) { - return TETHERING_WIFI; - } else if (cfg.isWigig(iface)) { - return TETHERING_WIGIG; - } else if (cfg.isWifiP2p(iface)) { - return TETHERING_WIFI_P2P; - } else if (cfg.isUsb(iface)) { - return TETHERING_USB; - } else if (cfg.isBluetooth(iface)) { - return TETHERING_BLUETOOTH; - } else if (cfg.isNcm(iface)) { - return TETHERING_NCM; - } - return TETHERING_INVALID; - } - - void interfaceAdded(String iface) { - if (VDBG) Log.d(TAG, "interfaceAdded " + iface); - synchronized (mPublicSync) { - maybeTrackNewInterfaceLocked(iface); - } - } - - void interfaceRemoved(String iface) { - if (VDBG) Log.d(TAG, "interfaceRemoved " + iface); - synchronized (mPublicSync) { - stopTrackingInterfaceLocked(iface); - } - } - - void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { - mHandler.post(() -> { - final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get( - request.tetheringType); - // If tethering is already enabled with a different request, - // disable before re-enabling. - if (unfinishedRequest != null - && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) { - enableTetheringInternal(request.tetheringType, false /* disabled */, null); - mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType); - } - mActiveTetheringRequests.put(request.tetheringType, request); - - if (request.exemptFromEntitlementCheck) { - mEntitlementMgr.setExemptedDownstreamType(request.tetheringType); - } else { - mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, - request.showProvisioningUi); - } - enableTetheringInternal(request.tetheringType, true /* enabled */, listener); - }); - } - - void stopTethering(int type) { - mHandler.post(() -> { - mActiveTetheringRequests.remove(type); - - enableTetheringInternal(type, false /* disabled */, null); - mEntitlementMgr.stopProvisioningIfNeeded(type); - }); - } - - /** - * Enables or disables tethering for the given type. If provisioning is required, it will - * schedule provisioning rechecks for the specified interface. - */ - private void enableTetheringInternal(int type, boolean enable, - final IIntResultListener listener) { - int result = TETHER_ERROR_NO_ERROR; - switch (type) { - case TETHERING_WIFI: - result = setWifiTethering(enable); - break; - case TETHERING_USB: - result = setUsbTethering(enable); - break; - case TETHERING_BLUETOOTH: - setBluetoothTethering(enable, listener); - break; - case TETHERING_NCM: - result = setNcmTethering(enable); - break; - case TETHERING_ETHERNET: - result = setEthernetTethering(enable); - break; - default: - Log.w(TAG, "Invalid tether type."); - result = TETHER_ERROR_UNKNOWN_TYPE; - } - - // The result of Bluetooth tethering will be sent by #setBluetoothTethering. - if (type != TETHERING_BLUETOOTH) { - sendTetherResult(listener, result, type); - } - } - - private void sendTetherResult(final IIntResultListener listener, final int result, - final int type) { - if (listener != null) { - try { - listener.onResult(result); - } catch (RemoteException e) { } - } - - // If changing tethering fail, remove corresponding request - // no matter who trigger the start/stop. - if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type); - } - - private int setWifiTethering(final boolean enable) { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mPublicSync) { - final WifiManager mgr = getWifiManager(); - if (mgr == null) { - mLog.e("setWifiTethering: failed to get WifiManager!"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */)) - || (!enable && mgr.stopSoftAp())) { - mWifiTetherRequested = enable; - return TETHER_ERROR_NO_ERROR; - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - return TETHER_ERROR_INTERNAL_ERROR; - } - - private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) { - final BluetoothAdapter adapter = mDeps.getBluetoothAdapter(); - if (adapter == null || !adapter.isEnabled()) { - Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " - + (adapter == null)); - sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH); - return; - } - - adapter.getProfileProxy(mContext, new ServiceListener() { - @Override - public void onServiceDisconnected(int profile) { } - - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - // Clear identify is fine because caller already pass tethering permission at - // ConnectivityService#startTethering()(or stopTethering) before the control comes - // here. Bluetooth will check tethering permission again that there is - // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get - // caller's package name for permission check. - // Calling BluetoothPan#setBluetoothTethering() here means the package name always - // be system server. If calling identity is not cleared, that package's uid might - // not match calling uid and end up in permission denied. - final long identityToken = Binder.clearCallingIdentity(); - try { - ((BluetoothPan) proxy).setBluetoothTethering(enable); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - // TODO: Enabling bluetooth tethering can fail asynchronously here. - // We should figure out a way to bubble up that failure instead of sending success. - final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) - ? TETHER_ERROR_NO_ERROR - : TETHER_ERROR_INTERNAL_ERROR; - sendTetherResult(listener, result, TETHERING_BLUETOOTH); - adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); - } - }, BluetoothProfile.PAN); - } - - private int setEthernetTethering(final boolean enable) { - final EthernetManager em = (EthernetManager) mContext.getSystemService( - Context.ETHERNET_SERVICE); - synchronized (mPublicSync) { - if (enable) { - if (mEthernetCallback != null) { - Log.d(TAG, "Ethernet tethering already started"); - return TETHER_ERROR_NO_ERROR; - } - - mEthernetCallback = new EthernetCallback(); - mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback); - } else { - stopEthernetTetheringLocked(); - } - } - return TETHER_ERROR_NO_ERROR; - } - - private void stopEthernetTetheringLocked() { - if (mConfiguredEthernetIface != null) { - stopTrackingInterfaceLocked(mConfiguredEthernetIface); - mConfiguredEthernetIface = null; - } - if (mEthernetCallback != null) { - mEthernetIfaceRequest.release(); - mEthernetCallback = null; - mEthernetIfaceRequest = null; - } - } - - private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback { - @Override - public void onAvailable(String iface) { - synchronized (mPublicSync) { - if (this != mEthernetCallback) { - // Ethernet callback arrived after Ethernet tethering stopped. Ignore. - return; - } - maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET); - changeInterfaceState(iface, IpServer.STATE_TETHERED); - mConfiguredEthernetIface = iface; - } - } - - @Override - public void onUnavailable() { - synchronized (mPublicSync) { - if (this != mEthernetCallback) { - // onAvailable called after stopping Ethernet tethering. - return; - } - stopEthernetTetheringLocked(); - } - } - } - - int tether(String iface) { - return tether(iface, IpServer.STATE_TETHERED); - } - - private int tether(String iface, int requestedState) { - if (DBG) Log.d(TAG, "Tethering " + iface); - synchronized (mPublicSync) { - TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring"); - return TETHER_ERROR_UNKNOWN_IFACE; - } - // Ignore the error status of the interface. If the interface is available, - // the errors are referring to past tethering attempts anyway. - if (tetherState.lastState != IpServer.STATE_AVAILABLE) { - Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); - return TETHER_ERROR_UNAVAIL_IFACE; - } - // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet - // processed, this will be a no-op and it will not return an error. - // - // This code cannot race with untether() because they both synchronize on mPublicSync. - // TODO: reexamine the threading and messaging model to totally remove mPublicSync. - final int type = tetherState.ipServer.interfaceType(); - final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null); - if (request != null) { - mActiveTetheringRequests.delete(type); - } - tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0, - request); - return TETHER_ERROR_NO_ERROR; - } - } - - int untether(String iface) { - if (DBG) Log.d(TAG, "Untethering " + iface); - synchronized (mPublicSync) { - TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring"); - return TETHER_ERROR_UNKNOWN_IFACE; - } - if (!tetherState.isCurrentlyServing()) { - Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring"); - return TETHER_ERROR_UNAVAIL_IFACE; - } - tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED); - return TETHER_ERROR_NO_ERROR; - } - } - - void untetherAll() { - stopTethering(TETHERING_WIFI); - stopTethering(TETHERING_WIFI_P2P); - stopTethering(TETHERING_USB); - stopTethering(TETHERING_BLUETOOTH); - stopTethering(TETHERING_ETHERNET); - } - - int getLastTetherError(String iface) { - synchronized (mPublicSync) { - TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface - + ", ignoring"); - return TETHER_ERROR_UNKNOWN_IFACE; - } - return tetherState.lastError; - } - } - - private boolean isProvisioningNeededButUnavailable() { - return isTetherProvisioningRequired() && !doesEntitlementPackageExist(); - } - - boolean isTetherProvisioningRequired() { - final TetheringConfiguration cfg = mConfig; - return mEntitlementMgr.isTetherProvisioningRequired(cfg); - } - - private boolean doesEntitlementPackageExist() { - // provisioningApp must contain package and class name. - if (mConfig.provisioningApp.length != 2) { - return false; - } - - final PackageManager pm = mContext.getPackageManager(); - try { - pm.getPackageInfo(mConfig.provisioningApp[0], GET_ACTIVITIES); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - return true; - } - - // TODO: Figure out how to update for local hotspot mode interfaces. - private void sendTetherStateChangedBroadcast() { - if (!isTetheringSupported()) return; - - final ArrayList<String> availableList = new ArrayList<>(); - final ArrayList<String> tetherList = new ArrayList<>(); - final ArrayList<String> localOnlyList = new ArrayList<>(); - final ArrayList<String> erroredList = new ArrayList<>(); - final ArrayList<Integer> lastErrorList = new ArrayList<>(); - - final TetheringConfiguration cfg = mConfig; - mTetherStatesParcel = new TetherStatesParcel(); - - int downstreamTypesMask = DOWNSTREAM_NONE; - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - String iface = mTetherStates.keyAt(i); - if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { - erroredList.add(iface); - lastErrorList.add(tetherState.lastError); - } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) { - availableList.add(iface); - } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) { - localOnlyList.add(iface); - } else if (tetherState.lastState == IpServer.STATE_TETHERED) { - if (cfg.isUsb(iface)) { - downstreamTypesMask |= (1 << TETHERING_USB); - } else if (cfg.isWifi(iface)) { - downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if (cfg.isBluetooth(iface)) { - downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); - } - tetherList.add(iface); - } - } - } - - mTetherStatesParcel.availableList = availableList.toArray(new String[0]); - mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]); - mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]); - mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]); - mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()]; - Iterator<Integer> iterator = lastErrorList.iterator(); - for (int i = 0; i < lastErrorList.size(); i++) { - mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue(); - } - reportTetherStateChanged(mTetherStatesParcel); - - final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED); - bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList); - bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList); - bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList); - bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList); - mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL); - if (DBG) { - Log.d(TAG, String.format( - "sendTetherStateChangedBroadcast %s=[%s] %s=[%s] %s=[%s] %s=[%s]", - "avail", TextUtils.join(",", availableList), - "local_only", TextUtils.join(",", localOnlyList), - "tether", TextUtils.join(",", tetherList), - "error", TextUtils.join(",", erroredList))); - } - - mNotificationUpdater.onDownstreamChanged(downstreamTypesMask); - } - - private class StateReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context content, Intent intent) { - final String action = intent.getAction(); - if (action == null) return; - - if (action.equals(UsbManager.ACTION_USB_STATE)) { - handleUsbAction(intent); - } else if (action.equals(CONNECTIVITY_ACTION)) { - handleConnectivityAction(intent); - } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { - handleWifiApAction(intent); - } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { - handleWifiP2pAction(intent); - } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { - mLog.log("OBSERVED configuration changed"); - updateConfiguration(); - } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) { - mLog.log("OBSERVED user restrictions changed"); - handleUserRestrictionAction(); - } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) { - mLog.log("OBSERVED data saver changed"); - handleDataSaverChanged(); - } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) { - untetherAll(); - } - } - - private void handleConnectivityAction(Intent intent) { - final NetworkInfo networkInfo = - (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO); - if (networkInfo == null - || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) { - return; - } - - if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION: " + networkInfo.toString()); - mTetherMainSM.sendMessage(TetherMainSM.CMD_UPSTREAM_CHANGED); - } - - private void handleUsbAction(Intent intent) { - final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false); - final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false); - final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false); - final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false); - - mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s", - usbConnected, usbConfigured, rndisEnabled)); - - // There are three types of ACTION_USB_STATE: - // - // - DISCONNECTED (USB_CONNECTED and USB_CONFIGURED are 0) - // Meaning: USB connection has ended either because of - // software reset or hard unplug. - // - // - CONNECTED (USB_CONNECTED is 1, USB_CONFIGURED is 0) - // Meaning: the first stage of USB protocol handshake has - // occurred but it is not complete. - // - // - CONFIGURED (USB_CONNECTED and USB_CONFIGURED are 1) - // Meaning: the USB handshake is completely done and all the - // functions are ready to use. - // - // For more explanation, see b/62552150 . - synchronized (Tethering.this.mPublicSync) { - if (!usbConnected && mRndisEnabled) { - // Turn off tethering if it was enabled and there is a disconnect. - tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB); - mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB); - } else if (usbConfigured && rndisEnabled) { - // Tether if rndis is enabled and usb is configured. - tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); - } else if (usbConnected && ncmEnabled) { - tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM); - } - mRndisEnabled = usbConfigured && rndisEnabled; - } - } - - private void handleWifiApAction(Intent intent) { - final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); - final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); - final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED); - - synchronized (Tethering.this.mPublicSync) { - switch (curState) { - case WifiManager.WIFI_AP_STATE_ENABLING: - // We can see this state on the way to both enabled and failure states. - break; - case WifiManager.WIFI_AP_STATE_ENABLED: - enableWifiIpServingLocked(ifname, ipmode); - break; - case WifiManager.WIFI_AP_STATE_DISABLING: - // We can see this state on the way to disabled. - break; - case WifiManager.WIFI_AP_STATE_DISABLED: - case WifiManager.WIFI_AP_STATE_FAILED: - default: - disableWifiIpServingLocked(ifname, curState); - break; - } - } - } - - private boolean isGroupOwner(WifiP2pGroup group) { - return group != null && group.isGroupOwner() - && !TextUtils.isEmpty(group.getInterface()); - } - - private void handleWifiP2pAction(Intent intent) { - if (mConfig.isWifiP2pLegacyTetheringMode()) return; - - final WifiP2pInfo p2pInfo = - (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO); - final WifiP2pGroup group = - (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP); - - if (VDBG) { - Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group); - } - - synchronized (Tethering.this.mPublicSync) { - // if no group is formed, bring it down if needed. - if (p2pInfo == null || !p2pInfo.groupFormed) { - disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface); - mWifiP2pTetherInterface = null; - return; - } - - // If there is a group but the device is not the owner, bail out. - if (!isGroupOwner(group)) return; - - // If already serving from the correct interface, nothing to do. - if (group.getInterface().equals(mWifiP2pTetherInterface)) return; - - // If already serving from another interface, turn it down first. - if (!TextUtils.isEmpty(mWifiP2pTetherInterface)) { - mLog.w("P2P tethered interface " + mWifiP2pTetherInterface - + "is different from current interface " - + group.getInterface() + ", re-tether it"); - disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface); - } - - // Finally bring up serving on the new interface - mWifiP2pTetherInterface = group.getInterface(); - enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY); - } - } - - private void handleUserRestrictionAction() { - mTetheringRestriction.onUserRestrictionsChanged(); - } - - private void handleDataSaverChanged() { - final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus() - != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; - - if (mDataSaverEnabled == isDataSaverEnabled) return; - - mDataSaverEnabled = isDataSaverEnabled; - if (mDataSaverEnabled) { - untetherAll(); - } - } - } - - @VisibleForTesting - boolean isTetheringActive() { - return mActiveTetheringRequests.size() > 0; - } - - @VisibleForTesting - protected static class UserRestrictionActionListener { - private final UserManager mUserMgr; - private final Tethering mWrapper; - private final TetheringNotificationUpdater mNotificationUpdater; - public boolean mDisallowTethering; - - public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, - @NonNull TetheringNotificationUpdater updater) { - mUserMgr = um; - mWrapper = wrapper; - mNotificationUpdater = updater; - mDisallowTethering = false; - } - - public void onUserRestrictionsChanged() { - // getUserRestrictions gets restriction for this process' user, which is the primary - // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary - // user. See UserManager.DISALLOW_CONFIG_TETHERING. - final Bundle restrictions = mUserMgr.getUserRestrictions(); - final boolean newlyDisallowed = - restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); - final boolean prevDisallowed = mDisallowTethering; - mDisallowTethering = newlyDisallowed; - - final boolean tetheringDisallowedChanged = (newlyDisallowed != prevDisallowed); - if (!tetheringDisallowedChanged) { - return; - } - - if (!newlyDisallowed) { - // Clear the restricted notification when user is allowed to have tethering - // function. - mNotificationUpdater.tetheringRestrictionLifted(); - return; - } - - if (mWrapper.isTetheringActive()) { - // Restricted notification is shown when tethering function is disallowed on - // user's device. - mNotificationUpdater.notifyTetheringDisabledByRestriction(); - - // Untether from all downstreams since tethering is disallowed. - mWrapper.untetherAll(); - } - // TODO(b/148139325): send tetheringSupported on restriction change - } - } - - private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) { - mLog.log("Canceling WiFi tethering request -" - + " type=" + tetheringType - + " interface=" + ifname - + " state=" + apState); - - if (!TextUtils.isEmpty(ifname)) { - final TetherState ts = mTetherStates.get(ifname); - if (ts != null) { - ts.ipServer.unwanted(); - return; - } - } - - for (int i = 0; i < mTetherStates.size(); i++) { - final IpServer ipServer = mTetherStates.valueAt(i).ipServer; - if (ipServer.interfaceType() == tetheringType) { - ipServer.unwanted(); - return; - } - } - - mLog.log("Error disabling Wi-Fi IP serving; " - + (TextUtils.isEmpty(ifname) ? "no interface name specified" - : "specified interface: " + ifname)); - } - - private void disableWifiIpServingLocked(String ifname, int apState) { - // Regardless of whether we requested this transition, the AP has gone - // down. Don't try to tether again unless we're requested to do so. - // TODO: Remove this altogether, once Wi-Fi reliably gives us an - // interface name with every broadcast. - mWifiTetherRequested = false; - - disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState); - } - - private void disableWifiP2pIpServingLockedIfNeeded(String ifname) { - if (TextUtils.isEmpty(ifname)) return; - - disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* fake */ 0); - } - - private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { - // Map wifiIpMode values to IpServer.Callback serving states, inferring - // from mWifiTetherRequested as a final "best guess". - final int ipServingMode; - switch (wifiIpMode) { - case IFACE_IP_MODE_TETHERED: - ipServingMode = IpServer.STATE_TETHERED; - break; - case IFACE_IP_MODE_LOCAL_ONLY: - ipServingMode = IpServer.STATE_LOCAL_ONLY; - break; - default: - mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode); - return; - } - - if (!TextUtils.isEmpty(ifname)) { - maybeTrackNewInterfaceLocked(ifname); - changeInterfaceState(ifname, ipServingMode); - } else { - mLog.e(String.format( - "Cannot enable IP serving in mode %s on missing interface name", - ipServingMode)); - } - } - - // TODO: Consider renaming to something more accurate in its description. - // This method: - // - allows requesting either tethering or local hotspot serving states - // - handles both enabling and disabling serving states - // - only tethers the first matching interface in listInterfaces() - // order of a given type - private void tetherMatchingInterfaces(int requestedState, int interfaceType) { - if (VDBG) { - Log.d(TAG, "tetherMatchingInterfaces(" + requestedState + ", " + interfaceType + ")"); - } - - String[] ifaces = null; - try { - ifaces = mNetd.interfaceGetList(); - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Error listing Interfaces", e); - return; - } - String chosenIface = null; - if (ifaces != null) { - for (String iface : ifaces) { - if (ifaceNameToType(iface) == interfaceType) { - chosenIface = iface; - break; - } - } - } - if (chosenIface == null) { - Log.e(TAG, "could not find iface of type " + interfaceType); - return; - } - - changeInterfaceState(chosenIface, requestedState); - } - - private void changeInterfaceState(String ifname, int requestedState) { - final int result; - switch (requestedState) { - case IpServer.STATE_UNAVAILABLE: - case IpServer.STATE_AVAILABLE: - result = untether(ifname); - break; - case IpServer.STATE_TETHERED: - case IpServer.STATE_LOCAL_ONLY: - result = tether(ifname, requestedState); - break; - default: - Log.wtf(TAG, "Unknown interface state: " + requestedState); - return; - } - if (result != TETHER_ERROR_NO_ERROR) { - Log.e(TAG, "unable start or stop tethering on iface " + ifname); - return; - } - } - - TetheringConfiguration getTetheringConfiguration() { - return mConfig; - } - - boolean hasTetherableConfiguration() { - final TetheringConfiguration cfg = mConfig; - final boolean hasDownstreamConfiguration = - (cfg.tetherableUsbRegexs.length != 0) - || (cfg.tetherableWifiRegexs.length != 0) - || (cfg.tetherableBluetoothRegexs.length != 0); - final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty() - || cfg.chooseUpstreamAutomatically; - - return hasDownstreamConfiguration && hasUpstreamConfiguration; - } - - // TODO - update callers to use getTetheringConfiguration(), - // which has only final members. - String[] getTetherableUsbRegexs() { - return copy(mConfig.tetherableUsbRegexs); - } - - String[] getTetherableWifiRegexs() { - return copy(mConfig.tetherableWifiRegexs); - } - - String[] getTetherableBluetoothRegexs() { - return copy(mConfig.tetherableBluetoothRegexs); - } - - int setUsbTethering(boolean enable) { - if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")"); - UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - if (usbManager == null) { - mLog.e("setUsbTethering: failed to get UsbManager!"); - return TETHER_ERROR_SERVICE_UNAVAIL; - } - - synchronized (mPublicSync) { - usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS - : UsbManager.FUNCTION_NONE); - } - return TETHER_ERROR_NO_ERROR; - } - - private int setNcmTethering(boolean enable) { - if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")"); - UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - synchronized (mPublicSync) { - usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM - : UsbManager.FUNCTION_NONE); - } - return TETHER_ERROR_NO_ERROR; - } - - // TODO review API - figure out how to delete these entirely. - String[] getTetheredIfaces() { - ArrayList<String> list = new ArrayList<String>(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState == IpServer.STATE_TETHERED) { - list.add(mTetherStates.keyAt(i)); - } - } - } - return list.toArray(new String[list.size()]); - } - - String[] getTetherableIfaces() { - ArrayList<String> list = new ArrayList<String>(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState == IpServer.STATE_AVAILABLE) { - list.add(mTetherStates.keyAt(i)); - } - } - } - return list.toArray(new String[list.size()]); - } - - String[] getTetheredDhcpRanges() { - // TODO: this is only valid for the old DHCP server. Latest search suggests it is only used - // by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers. - return mConfig.legacyDhcpRanges; - } - - String[] getErroredIfaces() { - ArrayList<String> list = new ArrayList<String>(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { - list.add(mTetherStates.keyAt(i)); - } - } - } - return list.toArray(new String[list.size()]); - } - - private void logMessage(State state, int what) { - mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what))); - } - - private boolean upstreamWanted() { - if (!mForwardedDownstreams.isEmpty()) return true; - - synchronized (mPublicSync) { - return mWifiTetherRequested; - } - } - - // Needed because the canonical source of upstream truth is just the - // upstream interface set, |mCurrentUpstreamIfaceSet|. - private boolean pertainsToCurrentUpstream(UpstreamNetworkState ns) { - if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) { - for (String ifname : ns.linkProperties.getAllInterfaceNames()) { - if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) { - return true; - } - } - } - return false; - } - - class TetherMainSM extends StateMachine { - // an interface SM has requested Tethering/Local Hotspot - static final int EVENT_IFACE_SERVING_STATE_ACTIVE = BASE_MAIN_SM + 1; - // an interface SM has unrequested Tethering/Local Hotspot - static final int EVENT_IFACE_SERVING_STATE_INACTIVE = BASE_MAIN_SM + 2; - // upstream connection change - do the right thing - static final int CMD_UPSTREAM_CHANGED = BASE_MAIN_SM + 3; - // we don't have a valid upstream conn, check again after a delay - static final int CMD_RETRY_UPSTREAM = BASE_MAIN_SM + 4; - // Events from NetworkCallbacks that we process on the main state - // machine thread on behalf of the UpstreamNetworkMonitor. - static final int EVENT_UPSTREAM_CALLBACK = BASE_MAIN_SM + 5; - // we treated the error and want now to clear it - static final int CMD_CLEAR_ERROR = BASE_MAIN_SM + 6; - static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MAIN_SM + 7; - // Events from EntitlementManager to choose upstream again. - static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MAIN_SM + 8; - private final State mInitialState; - private final State mTetherModeAliveState; - - private final State mSetIpForwardingEnabledErrorState; - private final State mSetIpForwardingDisabledErrorState; - private final State mStartTetheringErrorState; - private final State mStopTetheringErrorState; - private final State mSetDnsForwardersErrorState; - - // This list is a little subtle. It contains all the interfaces that currently are - // requesting tethering, regardless of whether these interfaces are still members of - // mTetherStates. This allows us to maintain the following predicates: - // - // 1) mTetherStates contains the set of all currently existing, tetherable, link state up - // interfaces. - // 2) mNotifyList contains all state machines that may have outstanding tethering state - // that needs to be torn down. - // - // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList - // so that the garbage collector does not clean up the state machine before it has a chance - // to tear itself down. - private final ArrayList<IpServer> mNotifyList; - private final IPv6TetheringCoordinator mIPv6TetheringCoordinator; - private final OffloadWrapper mOffload; - - private static final int UPSTREAM_SETTLE_TIME_MS = 10000; - - TetherMainSM(String name, Looper looper, TetheringDependencies deps) { - super(name, looper); - - mInitialState = new InitialState(); - mTetherModeAliveState = new TetherModeAliveState(); - mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); - mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); - mStartTetheringErrorState = new StartTetheringErrorState(); - mStopTetheringErrorState = new StopTetheringErrorState(); - mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); - - addState(mInitialState); - addState(mTetherModeAliveState); - addState(mSetIpForwardingEnabledErrorState); - addState(mSetIpForwardingDisabledErrorState); - addState(mStartTetheringErrorState); - addState(mStopTetheringErrorState); - addState(mSetDnsForwardersErrorState); - - mNotifyList = new ArrayList<>(); - mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog); - mOffload = new OffloadWrapper(); - - setInitialState(mInitialState); - } - - class InitialState extends State { - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: { - final IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); - handleInterfaceServingStateActive(message.arg1, who); - transitionTo(mTetherModeAliveState); - break; - } - case EVENT_IFACE_SERVING_STATE_INACTIVE: { - final IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); - handleInterfaceServingStateInactive(who); - break; - } - case EVENT_IFACE_UPDATE_LINKPROPERTIES: - // Silently ignore these for now. - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - protected boolean turnOnMainTetherSettings() { - final TetheringConfiguration cfg = mConfig; - try { - mNetd.ipfwdEnableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e(e); - transitionTo(mSetIpForwardingEnabledErrorState); - return false; - } - - // TODO: Randomize DHCPv4 ranges, especially in hotspot mode. - // Legacy DHCP server is disabled if passed an empty ranges array - final String[] dhcpRanges = cfg.enableLegacyDhcpServer - ? cfg.legacyDhcpRanges : new String[0]; - try { - NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); - } catch (RemoteException | ServiceSpecificException e) { - try { - // Stop and retry. - mNetd.tetherStop(); - NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); - } catch (RemoteException | ServiceSpecificException ee) { - mLog.e(ee); - transitionTo(mStartTetheringErrorState); - return false; - } - } - mLog.log("SET main tether settings: ON"); - return true; - } - - protected boolean turnOffMainTetherSettings() { - try { - mNetd.tetherStop(); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e(e); - transitionTo(mStopTetheringErrorState); - return false; - } - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { - mLog.e(e); - transitionTo(mSetIpForwardingDisabledErrorState); - return false; - } - transitionTo(mInitialState); - mLog.log("SET main tether settings: OFF"); - return true; - } - - protected void chooseUpstreamType(boolean tryCell) { - // We rebuild configuration on ACTION_CONFIGURATION_CHANGED, but we - // do not currently know how to watch for changes in DUN settings. - maybeDunSettingChanged(); - - final TetheringConfiguration config = mConfig; - final UpstreamNetworkState ns = (config.chooseUpstreamAutomatically) - ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream() - : mUpstreamNetworkMonitor.selectPreferredUpstreamType( - config.preferredUpstreamIfaceTypes); - if (ns == null) { - if (tryCell) { - mUpstreamNetworkMonitor.registerMobileNetworkRequest(); - // We think mobile should be coming up; don't set a retry. - } else { - sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); - } - } - setUpstreamNetwork(ns); - final Network newUpstream = (ns != null) ? ns.network : null; - if (mTetherUpstream != newUpstream) { - mTetherUpstream = newUpstream; - mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream); - reportUpstreamChanged(ns); - } - } - - protected void setUpstreamNetwork(UpstreamNetworkState ns) { - InterfaceSet ifaces = null; - if (ns != null) { - // Find the interface with the default IPv4 route. It may be the - // interface described by linkProperties, or one of the interfaces - // stacked on top of it. - mLog.i("Looking for default routes on: " + ns.linkProperties); - ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns); - mLog.i("Found upstream interface(s): " + ifaces); - } - - if (ifaces != null) { - setDnsForwarders(ns.network, ns.linkProperties); - } - notifyDownstreamsOfNewUpstreamIface(ifaces); - if (ns != null && pertainsToCurrentUpstream(ns)) { - // If we already have UpstreamNetworkState for this network update it immediately. - handleNewUpstreamNetworkState(ns); - } else if (mCurrentUpstreamIfaceSet == null) { - // There are no available upstream networks. - handleNewUpstreamNetworkState(null); - } - } - - protected void setDnsForwarders(final Network network, final LinkProperties lp) { - // TODO: Set v4 and/or v6 DNS per available connectivity. - final Collection<InetAddress> dnses = lp.getDnsServers(); - // TODO: Properly support the absence of DNS servers. - final String[] dnsServers; - if (dnses != null && !dnses.isEmpty()) { - dnsServers = new String[dnses.size()]; - int i = 0; - for (InetAddress dns : dnses) { - dnsServers[i++] = dns.getHostAddress(); - } - } else { - dnsServers = mConfig.defaultIPv4DNS; - } - final int netId = (network != null) ? network.getNetId() : NETID_UNSET; - try { - mNetd.tetherDnsSet(netId, dnsServers); - mLog.log(String.format( - "SET DNS forwarders: network=%s dnsServers=%s", - network, Arrays.toString(dnsServers))); - } catch (RemoteException | ServiceSpecificException e) { - // TODO: Investigate how this can fail and what exactly - // happens if/when such failures occur. - mLog.e("setting DNS forwarders failed, " + e); - transitionTo(mSetDnsForwardersErrorState); - } - } - - protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { - mCurrentUpstreamIfaceSet = ifaces; - for (IpServer ipServer : mNotifyList) { - ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces); - } - } - - protected void handleNewUpstreamNetworkState(UpstreamNetworkState ns) { - mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns); - mOffload.updateUpstreamNetworkState(ns); - } - - private void handleInterfaceServingStateActive(int mode, IpServer who) { - if (mNotifyList.indexOf(who) < 0) { - mNotifyList.add(who); - mIPv6TetheringCoordinator.addActiveDownstream(who, mode); - } - - if (mode == IpServer.STATE_TETHERED) { - // No need to notify OffloadController just yet as there are no - // "offload-able" prefixes to pass along. This will handled - // when the TISM informs Tethering of its LinkProperties. - mForwardedDownstreams.add(who); - } else { - mOffload.excludeDownstreamInterface(who.interfaceName()); - mForwardedDownstreams.remove(who); - } - - // If this is a Wi-Fi interface, notify WifiManager of the active serving state. - if (who.interfaceType() == TETHERING_WIFI) { - final WifiManager mgr = getWifiManager(); - final String iface = who.interfaceName(); - switch (mode) { - case IpServer.STATE_TETHERED: - mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED); - break; - case IpServer.STATE_LOCAL_ONLY: - mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY); - break; - default: - Log.wtf(TAG, "Unknown active serving mode: " + mode); - break; - } - } - } - - private void handleInterfaceServingStateInactive(IpServer who) { - mNotifyList.remove(who); - mIPv6TetheringCoordinator.removeActiveDownstream(who); - mOffload.excludeDownstreamInterface(who.interfaceName()); - mForwardedDownstreams.remove(who); - updateConnectedClients(null /* wifiClients */); - - // If this is a Wi-Fi interface, tell WifiManager of any errors - // or the inactive serving state. - if (who.interfaceType() == TETHERING_WIFI) { - if (who.lastError() != TETHER_ERROR_NO_ERROR) { - getWifiManager().updateInterfaceIpState( - who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR); - } else { - getWifiManager().updateInterfaceIpState( - who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED); - } - } - } - - @VisibleForTesting - void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { - if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { - mOffload.sendOffloadExemptPrefixes((Set<IpPrefix>) o); - return; - } - - final UpstreamNetworkState ns = (UpstreamNetworkState) o; - switch (arg1) { - case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - mPrivateAddressCoordinator.updateUpstreamPrefix(ns); - break; - case UpstreamNetworkMonitor.EVENT_ON_LOST: - mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network); - break; - } - - if (ns == null || !pertainsToCurrentUpstream(ns)) { - // TODO: In future, this is where upstream evaluation and selection - // could be handled for notifications which include sufficient data. - // For example, after CONNECTIVITY_ACTION listening is removed, here - // is where we could observe a Wi-Fi network becoming available and - // passing validation. - if (mCurrentUpstreamIfaceSet == null) { - // If we have no upstream interface, try to run through upstream - // selection again. If, for example, IPv4 connectivity has shown up - // after IPv6 (e.g., 464xlat became available) we want the chance to - // notice and act accordingly. - chooseUpstreamType(false); - } - return; - } - - switch (arg1) { - case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES: - if (ns.network.equals(mTetherUpstream)) { - mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities); - } - handleNewUpstreamNetworkState(ns); - break; - case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - chooseUpstreamType(false); - break; - case UpstreamNetworkMonitor.EVENT_ON_LOST: - // TODO: Re-evaluate possible upstreams. Currently upstream - // reevaluation is triggered via received CONNECTIVITY_ACTION - // broadcasts that result in being passed a - // TetherMainSM.CMD_UPSTREAM_CHANGED. - handleNewUpstreamNetworkState(null); - break; - default: - mLog.e("Unknown arg1 value: " + arg1); - break; - } - } - - class TetherModeAliveState extends State { - boolean mUpstreamWanted = false; - boolean mTryCell = true; - - @Override - public void enter() { - // If turning on main tether settings fails, we have already - // transitioned to an error state; exit early. - if (!turnOnMainTetherSettings()) { - return; - } - - mPrivateAddressCoordinator.maybeRemoveDeprecatedUpstreams(); - mUpstreamNetworkMonitor.startObserveAllNetworks(); - - // TODO: De-duplicate with updateUpstreamWanted() below. - if (upstreamWanted()) { - mUpstreamWanted = true; - mOffload.start(); - chooseUpstreamType(true); - mTryCell = false; - } - - // TODO: Check the upstream interface if it is managed by BPF offload. - mBpfCoordinator.startPolling(); - } - - @Override - public void exit() { - mOffload.stop(); - mUpstreamNetworkMonitor.stop(); - notifyDownstreamsOfNewUpstreamIface(null); - handleNewUpstreamNetworkState(null); - if (mTetherUpstream != null) { - mTetherUpstream = null; - reportUpstreamChanged(null); - } - mBpfCoordinator.stopPolling(); - } - - private boolean updateUpstreamWanted() { - final boolean previousUpstreamWanted = mUpstreamWanted; - mUpstreamWanted = upstreamWanted(); - if (mUpstreamWanted != previousUpstreamWanted) { - if (mUpstreamWanted) { - mOffload.start(); - } else { - mOffload.stop(); - } - } - return previousUpstreamWanted; - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - boolean retValue = true; - switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: { - IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); - handleInterfaceServingStateActive(message.arg1, who); - who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, - mCurrentUpstreamIfaceSet); - // If there has been a change and an upstream is now - // desired, kick off the selection process. - final boolean previousUpstreamWanted = updateUpstreamWanted(); - if (!previousUpstreamWanted && mUpstreamWanted) { - chooseUpstreamType(true); - } - break; - } - case EVENT_IFACE_SERVING_STATE_INACTIVE: { - IpServer who = (IpServer) message.obj; - if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); - handleInterfaceServingStateInactive(who); - - if (mNotifyList.isEmpty()) { - // This transitions us out of TetherModeAliveState, - // either to InitialState or an error state. - turnOffMainTetherSettings(); - break; - } - - if (DBG) { - Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() - + " live requests:"); - for (IpServer o : mNotifyList) { - Log.d(TAG, " " + o); - } - } - // If there has been a change and an upstream is no - // longer desired, release any mobile requests. - final boolean previousUpstreamWanted = updateUpstreamWanted(); - if (previousUpstreamWanted && !mUpstreamWanted) { - mUpstreamNetworkMonitor.releaseMobileNetworkRequest(); - } - break; - } - case EVENT_IFACE_UPDATE_LINKPROPERTIES: { - final LinkProperties newLp = (LinkProperties) message.obj; - if (message.arg1 == IpServer.STATE_TETHERED) { - mOffload.updateDownstreamLinkProperties(newLp); - } else { - mOffload.excludeDownstreamInterface(newLp.getInterfaceName()); - } - break; - } - case EVENT_UPSTREAM_PERMISSION_CHANGED: - case CMD_UPSTREAM_CHANGED: - updateUpstreamWanted(); - if (!mUpstreamWanted) break; - - // Need to try DUN immediately if Wi-Fi goes down. - chooseUpstreamType(true); - mTryCell = false; - break; - case CMD_RETRY_UPSTREAM: - updateUpstreamWanted(); - if (!mUpstreamWanted) break; - - chooseUpstreamType(mTryCell); - mTryCell = !mTryCell; - break; - case EVENT_UPSTREAM_CALLBACK: { - updateUpstreamWanted(); - if (mUpstreamWanted) { - handleUpstreamNetworkMonitorCallback(message.arg1, message.obj); - } - break; - } - default: - retValue = false; - break; - } - return retValue; - } - } - - class ErrorState extends State { - private int mErrorNotification; - - @Override - public boolean processMessage(Message message) { - boolean retValue = true; - switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: - IpServer who = (IpServer) message.obj; - who.sendMessage(mErrorNotification); - break; - case CMD_CLEAR_ERROR: - mErrorNotification = TETHER_ERROR_NO_ERROR; - transitionTo(mInitialState); - break; - default: - retValue = false; - } - return retValue; - } - - void notify(int msgType) { - mErrorNotification = msgType; - for (IpServer ipServer : mNotifyList) { - ipServer.sendMessage(msgType); - } - } - - } - - class SetIpForwardingEnabledErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in setIpForwardingEnabled"); - notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR); - } - } - - class SetIpForwardingDisabledErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in setIpForwardingDisabled"); - notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR); - } - } - - class StartTetheringErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in startTethering"); - notify(IpServer.CMD_START_TETHERING_ERROR); - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { } - } - } - - class StopTetheringErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in stopTethering"); - notify(IpServer.CMD_STOP_TETHERING_ERROR); - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { } - } - } - - class SetDnsForwardersErrorState extends ErrorState { - @Override - public void enter() { - Log.e(TAG, "Error in setDnsForwarders"); - notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR); - try { - mNetd.tetherStop(); - } catch (RemoteException | ServiceSpecificException e) { } - try { - mNetd.ipfwdDisableForwarding(TAG); - } catch (RemoteException | ServiceSpecificException e) { } - } - } - - // A wrapper class to handle multiple situations where several calls to - // the OffloadController need to happen together. - // - // TODO: This suggests that the interface between OffloadController and - // Tethering is in need of improvement. Refactor these calls into the - // OffloadController implementation. - class OffloadWrapper { - public void start() { - final int status = mOffloadController.start() ? TETHER_HARDWARE_OFFLOAD_STARTED - : TETHER_HARDWARE_OFFLOAD_FAILED; - updateOffloadStatus(status); - sendOffloadExemptPrefixes(); - } - - public void stop() { - mOffloadController.stop(); - updateOffloadStatus(TETHER_HARDWARE_OFFLOAD_STOPPED); - } - - public void updateUpstreamNetworkState(UpstreamNetworkState ns) { - mOffloadController.setUpstreamLinkProperties( - (ns != null) ? ns.linkProperties : null); - } - - public void updateDownstreamLinkProperties(LinkProperties newLp) { - // Update the list of offload-exempt prefixes before adding - // new prefixes on downstream interfaces to the offload HAL. - sendOffloadExemptPrefixes(); - mOffloadController.notifyDownstreamLinkProperties(newLp); - } - - public void excludeDownstreamInterface(String ifname) { - // This and other interfaces may be in local-only hotspot mode; - // resend all local prefixes to the OffloadController. - sendOffloadExemptPrefixes(); - mOffloadController.removeDownstreamInterface(ifname); - } - - public void sendOffloadExemptPrefixes() { - sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes()); - } - - public void sendOffloadExemptPrefixes(final Set<IpPrefix> localPrefixes) { - // Add in well-known minimum set. - PrefixUtils.addNonForwardablePrefixes(localPrefixes); - // Add tragically hardcoded prefixes. - localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX); - - // Maybe add prefixes or addresses for downstreams, depending on - // the IP serving mode of each. - for (IpServer ipServer : mNotifyList) { - final LinkProperties lp = ipServer.linkProperties(); - - switch (ipServer.servingMode()) { - case IpServer.STATE_UNAVAILABLE: - case IpServer.STATE_AVAILABLE: - // No usable LinkProperties in these states. - continue; - case IpServer.STATE_TETHERED: - // Only add IPv4 /32 and IPv6 /128 prefixes. The - // directly-connected prefixes will be sent as - // downstream "offload-able" prefixes. - for (LinkAddress addr : lp.getAllLinkAddresses()) { - final InetAddress ip = addr.getAddress(); - if (ip.isLinkLocalAddress()) continue; - localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip)); - } - break; - case IpServer.STATE_LOCAL_ONLY: - // Add prefixes covering all local IPs. - localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp)); - break; - } - } - - mOffloadController.setLocalPrefixes(localPrefixes); - } - - private void updateOffloadStatus(final int newStatus) { - if (newStatus == mOffloadStatus) return; - - mOffloadStatus = newStatus; - reportOffloadStatusChanged(mOffloadStatus); - } - } - } - - private void startTrackDefaultNetwork() { - mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(), - mEntitlementMgr); - } - - /** Get the latest value of the tethering entitlement check. */ - void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, - boolean showEntitlementUi) { - if (receiver == null) return; - - mHandler.post(() -> { - mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver, - showEntitlementUi); - }); - } - - /** Register tethering event callback */ - void registerTetheringEventCallback(ITetheringEventCallback callback) { - final boolean hasListPermission = - hasCallingPermission(NETWORK_SETTINGS) - || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK) - || hasCallingPermission(NETWORK_STACK); - mHandler.post(() -> { - mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission)); - final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); - parcel.tetheringSupported = isTetheringSupported(); - parcel.upstreamNetwork = mTetherUpstream; - parcel.config = mConfig.toStableParcelable(); - parcel.states = - mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel(); - parcel.tetheredClients = hasListPermission - ? mConnectedClientsTracker.getLastTetheredClients() - : Collections.emptyList(); - parcel.offloadStatus = mOffloadStatus; - try { - callback.onCallbackStarted(parcel); - } catch (RemoteException e) { - // Not really very much to do here. - } - }); - } - - private TetherStatesParcel emptyTetherStatesParcel() { - final TetherStatesParcel parcel = new TetherStatesParcel(); - parcel.availableList = new String[0]; - parcel.tetheredList = new String[0]; - parcel.localOnlyList = new String[0]; - parcel.erroredIfaceList = new String[0]; - parcel.lastErrorList = new int[0]; - - return parcel; - } - - private boolean hasCallingPermission(@NonNull String permission) { - return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; - } - - /** Unregister tethering event callback */ - void unregisterTetheringEventCallback(ITetheringEventCallback callback) { - mHandler.post(() -> { - mTetheringEventCallbacks.unregister(callback); - }); - } - - private void reportUpstreamChanged(UpstreamNetworkState ns) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - final Network network = (ns != null) ? ns.network : null; - final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null; - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - // Need to notify capabilities change after upstream network changed because new network's - // capabilities should be checked every time. - mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities); - } - - private void reportConfigurationChanged(TetheringConfigurationParcel config) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config); - // TODO(b/148139325): send tetheringSupported on configuration change - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - private void reportTetherStateChanged(TetherStatesParcel states) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(states); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - private void reportTetherClientsChanged(List<TetheredClient> clients) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - final CallbackCookie cookie = - (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i); - if (!cookie.hasListClientsPermission) continue; - mTetheringEventCallbacks.getBroadcastItem(i).onTetherClientsChanged(clients); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - private void reportOffloadStatusChanged(final int status) { - final int length = mTetheringEventCallbacks.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mTetheringEventCallbacks.getBroadcastItem(i).onOffloadStatusChanged(status); - } catch (RemoteException e) { - // Not really very much to do here. - } - } - } finally { - mTetheringEventCallbacks.finishBroadcast(); - } - } - - // if ro.tether.denied = true we default to no tethering - // gservices could set the secure setting to 1 though to enable it on a build where it - // had previously been turned off. - boolean isTetheringSupported() { - final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1; - final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; - final boolean tetherEnabledInSettings = tetherSupported - && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - - return tetherEnabledInSettings && hasTetherableConfiguration() - && !isProvisioningNeededButUnavailable(); - } - - void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { - // Binder.java closes the resource for us. - @SuppressWarnings("resource") - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump."); - return; - } - - pw.println("Tethering:"); - pw.increaseIndent(); - - pw.println("Configuration:"); - pw.increaseIndent(); - final TetheringConfiguration cfg = mConfig; - cfg.dump(pw); - pw.decreaseIndent(); - - pw.println("Entitlement:"); - pw.increaseIndent(); - mEntitlementMgr.dump(pw); - pw.decreaseIndent(); - - synchronized (mPublicSync) { - pw.println("Tether state:"); - pw.increaseIndent(); - for (int i = 0; i < mTetherStates.size(); i++) { - final String iface = mTetherStates.keyAt(i); - final TetherState tetherState = mTetherStates.valueAt(i); - pw.print(iface + " - "); - - switch (tetherState.lastState) { - case IpServer.STATE_UNAVAILABLE: - pw.print("UnavailableState"); - break; - case IpServer.STATE_AVAILABLE: - pw.print("AvailableState"); - break; - case IpServer.STATE_TETHERED: - pw.print("TetheredState"); - break; - case IpServer.STATE_LOCAL_ONLY: - pw.print("LocalHotspotState"); - break; - default: - pw.print("UnknownState"); - break; - } - pw.println(" - lastError = " + tetherState.lastError); - } - pw.println("Upstream wanted: " + upstreamWanted()); - pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet); - pw.decreaseIndent(); - } - - pw.println("Hardware offload:"); - pw.increaseIndent(); - mOffloadController.dump(pw); - pw.decreaseIndent(); - - pw.println("BPF offload:"); - pw.increaseIndent(); - mBpfCoordinator.dump(pw); - pw.decreaseIndent(); - - pw.println("Private address coordinator:"); - pw.increaseIndent(); - mPrivateAddressCoordinator.dump(pw); - pw.decreaseIndent(); - - pw.println("Log:"); - pw.increaseIndent(); - if (argsContain(args, "--short")) { - pw.println("<log removed for brevity>"); - } else { - mLog.dump(fd, pw, args); - } - pw.decreaseIndent(); - - pw.decreaseIndent(); - } - - private static boolean argsContain(String[] args, String target) { - for (String arg : args) { - if (target.equals(arg)) return true; - } - return false; - } - - private void updateConnectedClients(final List<WifiClient> wifiClients) { - if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } - } - - private IpServer.Callback makeControlCallback() { - return new IpServer.Callback() { - @Override - public void updateInterfaceState(IpServer who, int state, int lastError) { - notifyInterfaceStateChange(who, state, lastError); - } - - @Override - public void updateLinkProperties(IpServer who, LinkProperties newLp) { - notifyLinkPropertiesChanged(who, newLp); - } - - @Override - public void dhcpLeasesChanged() { - updateConnectedClients(null /* wifiClients */); - } - - @Override - public void requestEnableTethering(int tetheringType, boolean enabled) { - enableTetheringInternal(tetheringType, enabled, null); - } - }; - } - - // TODO: Move into TetherMainSM. - private void notifyInterfaceStateChange(IpServer who, int state, int error) { - final String iface = who.interfaceName(); - synchronized (mPublicSync) { - final TetherState tetherState = mTetherStates.get(iface); - if (tetherState != null && tetherState.ipServer.equals(who)) { - tetherState.lastState = state; - tetherState.lastError = error; - } else { - if (DBG) Log.d(TAG, "got notification from stale iface " + iface); - } - } - - mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); - - // If TetherMainSM is in ErrorState, TetherMainSM stays there. - // Thus we give a chance for TetherMainSM to recover to InitialState - // by sending CMD_CLEAR_ERROR - if (error == TETHER_ERROR_INTERNAL_ERROR) { - mTetherMainSM.sendMessage(TetherMainSM.CMD_CLEAR_ERROR, who); - } - int which; - switch (state) { - case IpServer.STATE_UNAVAILABLE: - case IpServer.STATE_AVAILABLE: - which = TetherMainSM.EVENT_IFACE_SERVING_STATE_INACTIVE; - break; - case IpServer.STATE_TETHERED: - case IpServer.STATE_LOCAL_ONLY: - which = TetherMainSM.EVENT_IFACE_SERVING_STATE_ACTIVE; - break; - default: - Log.wtf(TAG, "Unknown interface state: " + state); - return; - } - mTetherMainSM.sendMessage(which, state, 0, who); - sendTetherStateChangedBroadcast(); - } - - private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) { - final String iface = who.interfaceName(); - final int state; - synchronized (mPublicSync) { - final TetherState tetherState = mTetherStates.get(iface); - if (tetherState != null && tetherState.ipServer.equals(who)) { - state = tetherState.lastState; - } else { - mLog.log("got notification from stale iface " + iface); - return; - } - } - - mLog.log(String.format( - "OBSERVED LinkProperties update iface=%s state=%s lp=%s", - iface, IpServer.getStateString(state), newLp)); - final int which = TetherMainSM.EVENT_IFACE_UPDATE_LINKPROPERTIES; - mTetherMainSM.sendMessage(which, state, 0, newLp); - } - - private void maybeTrackNewInterfaceLocked(final String iface) { - // If we don't care about this type of interface, ignore. - final int interfaceType = ifaceNameToType(iface); - if (interfaceType == TETHERING_INVALID) { - mLog.log(iface + " is not a tetherable iface, ignoring"); - return; - } - maybeTrackNewInterfaceLocked(iface, interfaceType); - } - - private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) { - // If we have already started a TISM for this interface, skip. - if (mTetherStates.containsKey(iface)) { - mLog.log("active iface (" + iface + ") reported as added, ignoring"); - return; - } - - mLog.log("adding TetheringInterfaceStateMachine for: " + iface); - final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator, - makeControlCallback(), mConfig.enableLegacyDhcpServer, - mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator, - mDeps.getIpServerDependencies())); - mTetherStates.put(iface, tetherState); - tetherState.ipServer.start(); - } - - private void stopTrackingInterfaceLocked(final String iface) { - final TetherState tetherState = mTetherStates.get(iface); - if (tetherState == null) { - mLog.log("attempting to remove unknown iface (" + iface + "), ignoring"); - return; - } - tetherState.ipServer.stop(); - mLog.log("removing TetheringInterfaceStateMachine for: " + iface); - mTetherStates.remove(iface); - } - - private static String[] copy(String[] strarray) { - return Arrays.copyOf(strarray, strarray.length); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java deleted file mode 100644 index 799637c9b1c5..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.content.Context.TELEPHONY_SERVICE; -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; - -import android.content.Context; -import android.content.res.Resources; -import android.net.TetheringConfigurationParcel; -import android.net.util.SharedLog; -import android.provider.DeviceConfig; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.StringJoiner; - -/** - * A utility class to encapsulate the various tethering configuration elements. - * - * This configuration data includes elements describing upstream properties - * (preferred and required types of upstream connectivity as well as default - * DNS servers to use if none are available) and downstream properties (such - * as regular expressions use to match suitable downstream interfaces and the - * DHCPv4 ranges to use). - * - * @hide - */ -public class TetheringConfiguration { - private static final String TAG = TetheringConfiguration.class.getSimpleName(); - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - - // Default ranges used for the legacy DHCP server. - // USB is 192.168.42.1 and 255.255.255.0 - // Wifi is 192.168.43.1 and 255.255.255.0 - // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1 - // with 255.255.255.0 - // P2P is 192.168.49.1 and 255.255.255.0 - private static final String[] LEGACY_DHCP_DEFAULT_RANGE = { - "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254", - "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254", - "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254", - "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254", - }; - - private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"}; - - /** - * Override enabling BPF offload configuration for tethering. - */ - public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD = - "override_tether_enable_bpf_offload"; - - /** - * Use the old dnsmasq DHCP server for tethering instead of the framework implementation. - */ - public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = - "tether_enable_legacy_dhcp_server"; - - public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP = - "use_legacy_wifi_p2p_dedicated_ip"; - - /** - * Flag use to enable select all prefix ranges feature. - * TODO: Remove this flag if there are no problems after M-2020-12 rolls out. - */ - public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES = - "tether_enable_select_all_prefix_ranges"; - - /** - * Default value that used to periodic polls tether offload stats from tethering offload HAL - * to make the data warnings work. - */ - public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000; - - public final String[] tetherableUsbRegexs; - public final String[] tetherableWifiRegexs; - public final String[] tetherableWigigRegexs; - public final String[] tetherableWifiP2pRegexs; - public final String[] tetherableBluetoothRegexs; - public final String[] tetherableNcmRegexs; - public final boolean isDunRequired; - public final boolean chooseUpstreamAutomatically; - public final Collection<Integer> preferredUpstreamIfaceTypes; - public final String[] legacyDhcpRanges; - public final String[] defaultIPv4DNS; - public final boolean enableLegacyDhcpServer; - - public final String[] provisioningApp; - public final String provisioningAppNoUi; - public final int provisioningCheckPeriod; - public final String provisioningResponse; - - public final int activeDataSubId; - - private final int mOffloadPollInterval; - // TODO: Add to TetheringConfigurationParcel if required. - private final boolean mEnableBpfOffload; - private final boolean mEnableWifiP2pDedicatedIp; - - private final boolean mEnableSelectAllPrefixRange; - - public TetheringConfiguration(Context ctx, SharedLog log, int id) { - final SharedLog configLog = log.forSubComponent("config"); - - activeDataSubId = id; - Resources res = getResources(ctx, activeDataSubId); - - tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); - tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs); - // TODO: Evaluate deleting this altogether now that Wi-Fi always passes - // us an interface name. Careful consideration needs to be given to - // implications for Settings and for provisioning checks. - tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs); - tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs); - tetherableWifiP2pRegexs = getResourceStringArray( - res, R.array.config_tether_wifi_p2p_regexs); - tetherableBluetoothRegexs = getResourceStringArray( - res, R.array.config_tether_bluetooth_regexs); - - isDunRequired = checkDunRequired(ctx); - - chooseUpstreamAutomatically = getResourceBoolean( - res, R.bool.config_tether_upstream_automatic, false /** defaultValue */); - preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); - - legacyDhcpRanges = getLegacyDhcpRanges(res); - defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); - mEnableBpfOffload = getEnableBpfOffload(res); - enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); - - provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getResourceString(res, - R.string.config_mobile_hotspot_provision_app_no_ui); - provisioningCheckPeriod = getResourceInteger(res, - R.integer.config_mobile_hotspot_provision_check_period, - 0 /* No periodic re-check */); - provisioningResponse = getResourceString(res, - R.string.config_mobile_hotspot_provision_response); - - mOffloadPollInterval = getResourceInteger(res, - R.integer.config_tether_offload_poll_interval, - DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - - mEnableWifiP2pDedicatedIp = getResourceBoolean(res, - R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip, - false /* defaultValue */); - - // Flags should normally not be booleans, but this is a kill-switch flag that is only used - // to turn off the feature, so binary rollback problems do not apply. - mEnableSelectAllPrefixRange = getDeviceConfigBoolean( - TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */); - - configLog.log(toString()); - } - - /** Check whether input interface belong to usb.*/ - public boolean isUsb(String iface) { - return matchesDownstreamRegexs(iface, tetherableUsbRegexs); - } - - /** Check whether input interface belong to wifi.*/ - public boolean isWifi(String iface) { - return matchesDownstreamRegexs(iface, tetherableWifiRegexs); - } - - /** Check whether input interface belong to wigig.*/ - public boolean isWigig(String iface) { - return matchesDownstreamRegexs(iface, tetherableWigigRegexs); - } - - /** Check whether this interface is Wifi P2P interface. */ - public boolean isWifiP2p(String iface) { - return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs); - } - - /** Check whether using legacy mode for wifi P2P. */ - public boolean isWifiP2pLegacyTetheringMode() { - return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0); - } - - /** Check whether input interface belong to bluetooth.*/ - public boolean isBluetooth(String iface) { - return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs); - } - - /** Check if interface is ncm */ - public boolean isNcm(String iface) { - return matchesDownstreamRegexs(iface, tetherableNcmRegexs); - } - - /** Check whether no ui entitlement application is available.*/ - public boolean hasMobileHotspotProvisionApp() { - return !TextUtils.isEmpty(provisioningAppNoUi); - } - - /** Check whether dedicated wifi p2p address is enabled. */ - public boolean shouldEnableWifiP2pDedicatedIp() { - return mEnableWifiP2pDedicatedIp; - } - - /** Does the dumping.*/ - public void dump(PrintWriter pw) { - pw.print("activeDataSubId: "); - pw.println(activeDataSubId); - - dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs); - dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); - dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs); - dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); - dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs); - - pw.print("isDunRequired: "); - pw.println(isDunRequired); - - pw.print("chooseUpstreamAutomatically: "); - pw.println(chooseUpstreamAutomatically); - pw.print("legacyPreredUpstreamIfaceTypes: "); - pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes))); - - dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges); - dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); - - pw.print("offloadPollInterval: "); - pw.println(mOffloadPollInterval); - - dumpStringArray(pw, "provisioningApp", provisioningApp); - pw.print("provisioningAppNoUi: "); - pw.println(provisioningAppNoUi); - - pw.print("enableBpfOffload: "); - pw.println(mEnableBpfOffload); - - pw.print("enableLegacyDhcpServer: "); - pw.println(enableLegacyDhcpServer); - - pw.print("enableWifiP2pDedicatedIp: "); - pw.println(mEnableWifiP2pDedicatedIp); - - pw.print("mEnableSelectAllPrefixRange: "); - pw.println(mEnableSelectAllPrefixRange); - } - - /** Returns the string representation of this object.*/ - public String toString() { - final StringJoiner sj = new StringJoiner(" "); - sj.add(String.format("activeDataSubId:%d", activeDataSubId)); - sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs))); - sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs))); - sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs))); - sj.add(String.format("tetherableBluetoothRegexs:%s", - makeString(tetherableBluetoothRegexs))); - sj.add(String.format("isDunRequired:%s", isDunRequired)); - sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically)); - sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval)); - sj.add(String.format("preferredUpstreamIfaceTypes:%s", - toIntArray(preferredUpstreamIfaceTypes))); - sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); - sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi)); - sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload)); - sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer)); - return String.format("TetheringConfiguration{%s}", sj.toString()); - } - - private static void dumpStringArray(PrintWriter pw, String label, String[] values) { - pw.print(label); - pw.print(": "); - - if (values != null) { - final StringJoiner sj = new StringJoiner(", ", "[", "]"); - for (String value : values) sj.add(value); - pw.print(sj.toString()); - } else { - pw.print("null"); - } - - pw.println(); - } - - private static String makeString(String[] strings) { - if (strings == null) return "null"; - final StringJoiner sj = new StringJoiner(",", "[", "]"); - for (String s : strings) sj.add(s); - return sj.toString(); - } - - /** Check whether dun is required. */ - public static boolean checkDunRequired(Context ctx) { - final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); - // TelephonyManager would uses the active data subscription, which should be the one used - // by tethering. - return (tm != null) ? tm.isTetheringApnRequired() : false; - } - - public int getOffloadPollInterval() { - return mOffloadPollInterval; - } - - public boolean isBpfOffloadEnabled() { - return mEnableBpfOffload; - } - - public boolean isSelectAllPrefixRangeEnabled() { - return mEnableSelectAllPrefixRange; - } - - private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { - final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); - final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); - for (int i : ifaceTypes) { - switch (i) { - case TYPE_MOBILE: - case TYPE_MOBILE_HIPRI: - if (dunRequired) continue; - break; - case TYPE_MOBILE_DUN: - if (!dunRequired) continue; - break; - } - upstreamIfaceTypes.add(i); - } - - // Fix up upstream interface types for DUN or mobile. NOTE: independent - // of the value of |dunRequired|, cell data of one form or another is - // *always* an upstream, regardless of the upstream interface types - // specified by configuration resources. - if (dunRequired) { - appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN); - } else { - // Do not modify if a cellular interface type is already present in the - // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no - // cellular interface types are found in the upstream interface types. - // This preserves backwards compatibility and prevents the DUN and default - // mobile types incorrectly appearing together, which could happen on - // previous releases in the common case where checkDunRequired returned - // DUN_UNSPECIFIED. - if (!containsOneOf(upstreamIfaceTypes, TYPE_MOBILE, TYPE_MOBILE_HIPRI)) { - upstreamIfaceTypes.add(TYPE_MOBILE); - upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI); - } - } - - // Always make sure our good friend Ethernet is present. - // TODO: consider unilaterally forcing this at the front. - prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET); - - return upstreamIfaceTypes; - } - - private static boolean matchesDownstreamRegexs(String iface, String[] regexs) { - for (String regex : regexs) { - if (iface.matches(regex)) return true; - } - return false; - } - - private static String[] getLegacyDhcpRanges(Resources res) { - final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range); - if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { - return fromResource; - } - return copy(LEGACY_DHCP_DEFAULT_RANGE); - } - - private static String getResourceString(Resources res, final int resId) { - try { - return res.getString(resId); - } catch (Resources.NotFoundException e) { - return ""; - } - } - - private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) { - try { - return res.getBoolean(resId); - } catch (Resources.NotFoundException e404) { - return defaultValue; - } - } - - private static String[] getResourceStringArray(Resources res, int resId) { - try { - final String[] strArray = res.getStringArray(resId); - return (strArray != null) ? strArray : EMPTY_STRING_ARRAY; - } catch (Resources.NotFoundException e404) { - return EMPTY_STRING_ARRAY; - } - } - - private static int getResourceInteger(Resources res, int resId, int defaultValue) { - try { - return res.getInteger(resId); - } catch (Resources.NotFoundException e404) { - return defaultValue; - } - } - - private boolean getEnableBpfOffload(final Resources res) { - // Get BPF offload config - // Priority 1: Device config - // Priority 2: Resource config - // Priority 3: Default value - final boolean defaultValue = getResourceBoolean( - res, R.bool.config_tether_enable_bpf_offload, true /** default value */); - - return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue); - } - - private boolean getEnableLegacyDhcpServer(final Resources res) { - return getResourceBoolean( - res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */) - || getDeviceConfigBoolean( - TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */); - } - - private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) { - // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead - // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the - // returned boolean value comes from device config or default value (because of null - // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java. - final String value = getDeviceConfigProperty(name); - return value != null ? Boolean.parseBoolean(value) : defaultValue; - } - - @VisibleForTesting - protected String getDeviceConfigProperty(String name) { - return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name); - } - - private Resources getResources(Context ctx, int subId) { - if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return getResourcesForSubIdWrapper(ctx, subId); - } else { - return ctx.getResources(); - } - } - - @VisibleForTesting - protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { - return SubscriptionManager.getResourcesForSubId(ctx, subId); - } - - private static String[] copy(String[] strarray) { - return Arrays.copyOf(strarray, strarray.length); - } - - private static void prependIfNotPresent(ArrayList<Integer> list, int value) { - if (list.contains(value)) return; - list.add(0, value); - } - - private static void appendIfNotPresent(ArrayList<Integer> list, int value) { - if (list.contains(value)) return; - list.add(value); - } - - private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) { - for (Integer value : values) { - if (list.contains(value)) return true; - } - return false; - } - - private static int[] toIntArray(Collection<Integer> values) { - final int[] result = new int[values.size()]; - int index = 0; - for (Integer value : values) { - result[index++] = value; - } - return result; - } - - /** - * Convert this TetheringConfiguration to a TetheringConfigurationParcel. - */ - public TetheringConfigurationParcel toStableParcelable() { - final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel(); - parcel.subId = activeDataSubId; - parcel.tetherableUsbRegexs = tetherableUsbRegexs; - parcel.tetherableWifiRegexs = tetherableWifiRegexs; - parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs; - parcel.isDunRequired = isDunRequired; - parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically; - - parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes); - - parcel.legacyDhcpRanges = legacyDhcpRanges; - parcel.defaultIPv4DNS = defaultIPv4DNS; - parcel.enableLegacyDhcpServer = enableLegacyDhcpServer; - parcel.provisioningApp = provisioningApp; - parcel.provisioningAppNoUi = provisioningAppNoUi; - parcel.provisioningCheckPeriod = provisioningCheckPeriod; - return parcel; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java deleted file mode 100644 index 45b914178e97..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import android.app.usage.NetworkStatsManager; -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.net.INetd; -import android.net.NetworkRequest; -import android.net.ip.IpServer; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.SystemProperties; -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -import com.android.internal.util.StateMachine; - -import java.util.ArrayList; - - -/** - * Capture tethering dependencies, for injection. - * - * @hide - */ -public abstract class TetheringDependencies { - /** - * Get a reference to the BpfCoordinator to be used by tethering. - */ - public @NonNull BpfCoordinator getBpfCoordinator( - @NonNull BpfCoordinator.Dependencies deps) { - return new BpfCoordinator(deps); - } - - /** - * Get a reference to the offload hardware interface to be used by tethering. - */ - public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { - return new OffloadHardwareInterface(h, log); - } - - /** - * Get a reference to the offload controller to be used by tethering. - */ - @NonNull - public OffloadController getOffloadController(@NonNull Handler h, - @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) { - final NetworkStatsManager statsManager = - (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE); - return new OffloadController(h, getOffloadHardwareInterface(h, log), - getContext().getContentResolver(), statsManager, log, deps); - } - - - /** - * Get a reference to the UpstreamNetworkMonitor to be used by tethering. - */ - public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target, - SharedLog log, int what) { - return new UpstreamNetworkMonitor(ctx, target, log, what); - } - - /** - * Get a reference to the IPv6TetheringCoordinator to be used by tethering. - */ - public IPv6TetheringCoordinator getIPv6TetheringCoordinator( - ArrayList<IpServer> notifyList, SharedLog log) { - return new IPv6TetheringCoordinator(notifyList, log); - } - - /** - * Get dependencies to be used by IpServer. - */ - public abstract IpServer.Dependencies getIpServerDependencies(); - - /** - * Indicates whether tethering is supported on the device. - */ - public boolean isTetheringSupported() { - return true; - } - - /** - * Get the NetworkRequest that should be fulfilled by the default network. - */ - public abstract NetworkRequest getDefaultNetworkRequest(); - - /** - * Get a reference to the EntitlementManager to be used by tethering. - */ - public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - return new EntitlementManager(ctx, h, log, callback); - } - - /** - * Generate a new TetheringConfiguration according to input sub Id. - */ - public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, - int subId) { - return new TetheringConfiguration(ctx, log, subId); - } - - /** - * Get a reference to INetd to be used by tethering. - */ - public INetd getINetd(Context context) { - return INetd.Stub.asInterface( - (IBinder) context.getSystemService(Context.NETD_SERVICE)); - } - - /** - * Get a reference to the TetheringNotificationUpdater to be used by tethering. - */ - public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx, - @NonNull final Looper looper) { - return new TetheringNotificationUpdater(ctx, looper); - } - - /** - * Get tethering thread looper. - */ - public abstract Looper getTetheringLooper(); - - /** - * Get Context of TetheringSerice. - */ - public abstract Context getContext(); - - /** - * Get a reference to BluetoothAdapter to be used by tethering. - */ - public abstract BluetoothAdapter getBluetoothAdapter(); - - /** - * Get SystemProperties which indicate whether tethering is denied. - */ - public boolean isTetheringDenied() { - return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true"); - } - - /** - * Get a reference to PrivateAddressCoordinator to be used by Tethering. - */ - public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx, - TetheringConfiguration cfg) { - return new PrivateAddressCoordinator(ctx, cfg); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java deleted file mode 100644 index ff38f717a121..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import android.annotation.Nullable; -import android.net.LinkProperties; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.util.InterfaceSet; - -import com.android.net.module.util.NetUtils; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * @hide - */ -public final class TetheringInterfaceUtils { - private static final InetAddress IN6ADDR_ANY = getByAddress( - new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); - private static final InetAddress INADDR_ANY = getByAddress(new byte[] {0, 0, 0, 0}); - - /** - * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. - * @return null if there is no usable interface, or a set of at least one interface otherwise. - */ - public static @Nullable InterfaceSet getTetheringInterfaces(UpstreamNetworkState ns) { - if (ns == null) { - return null; - } - - final LinkProperties lp = ns.linkProperties; - final String if4 = getInterfaceForDestination(lp, INADDR_ANY); - final String if6 = getIPv6Interface(ns); - - return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); - } - - /** - * Get the upstream interface for IPv6 tethering. - * @return null if there is no usable interface, or the interface name otherwise. - */ - public static @Nullable String getIPv6Interface(UpstreamNetworkState ns) { - // Broadly speaking: - // - // [1] does the upstream have an IPv6 default route? - // - // and - // - // [2] does the upstream have one or more global IPv6 /64s - // dedicated to this device? - // - // In lieu of Prefix Delegation and other evaluation of whether a - // prefix may or may not be dedicated to this device, for now just - // check whether the upstream is TRANSPORT_CELLULAR. This works - // because "[t]he 3GPP network allocates each default bearer a unique - // /64 prefix", per RFC 6459, Section 5.2. - final boolean canTether = - (ns != null) && (ns.network != null) - && (ns.linkProperties != null) && (ns.networkCapabilities != null) - // At least one upstream DNS server: - && ns.linkProperties.hasIpv6DnsServer() - // Minimal amount of IPv6 provisioning: - && ns.linkProperties.hasGlobalIpv6Address() - // Temporary approximation of "dedicated prefix": - && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); - - return canTether - ? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY) - : null; - } - - private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { - final RouteInfo ri = (lp != null) - ? NetUtils.selectBestRoute(lp.getAllRoutes(), dst) - : null; - return (ri != null) ? ri.getInterface() : null; - } - - private static InetAddress getByAddress(final byte[] addr) { - try { - return InetAddress.getByAddress(null, addr); - } catch (UnknownHostException e) { - throw new AssertionError("illegal address length" + addr.length); - } - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java deleted file mode 100644 index a0198cc9c126..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.text.TextUtils.isEmpty; - -import android.app.Notification; -import android.app.Notification.Action; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.net.NetworkCapabilities; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.UserHandle; -import android.provider.Settings; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.SparseArray; - -import androidx.annotation.DrawableRes; -import androidx.annotation.IntDef; -import androidx.annotation.IntRange; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * A class to display tethering-related notifications. - * - * <p>This class is not thread safe, it is intended to be used only from the tethering handler - * thread. However the constructor is an exception, as it is called on another thread ; - * therefore for thread safety all members of this class MUST either be final or initialized - * to their default value (0, false or null). - * - * @hide - */ -public class TetheringNotificationUpdater { - private static final String TAG = TetheringNotificationUpdater.class.getSimpleName(); - private static final String CHANNEL_ID = "TETHERING_STATUS"; - private static final String WIFI_DOWNSTREAM = "WIFI"; - private static final String USB_DOWNSTREAM = "USB"; - private static final String BLUETOOTH_DOWNSTREAM = "BT"; - @VisibleForTesting - static final String ACTION_DISABLE_TETHERING = - "com.android.server.connectivity.tethering.DISABLE_TETHERING"; - private static final boolean NOTIFY_DONE = true; - private static final boolean NO_NOTIFY = false; - @VisibleForTesting - static final int EVENT_SHOW_NO_UPSTREAM = 1; - // Id to update and cancel restricted notification. Must be unique within the tethering app. - @VisibleForTesting - static final int RESTRICTED_NOTIFICATION_ID = 1001; - // Id to update and cancel no upstream notification. Must be unique within the tethering app. - @VisibleForTesting - static final int NO_UPSTREAM_NOTIFICATION_ID = 1002; - // Id to update and cancel roaming notification. Must be unique within the tethering app. - @VisibleForTesting - static final int ROAMING_NOTIFICATION_ID = 1003; - @VisibleForTesting - static final int NO_ICON_ID = 0; - @VisibleForTesting - static final int DOWNSTREAM_NONE = 0; - // Refer to TelephonyManager#getSimCarrierId for more details about carrier id. - @VisibleForTesting - static final int VERIZON_CARRIER_ID = 1839; - private final Context mContext; - private final NotificationManager mNotificationManager; - private final NotificationChannel mChannel; - private final Handler mHandler; - - // WARNING : the constructor is called on a different thread. Thread safety therefore - // relies on these values being initialized to 0, false or null, and not any other value. If you - // need to change this, you will need to change the thread where the constructor is invoked, or - // to introduce synchronization. - // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. - // This value has to be made 1 2 and 4, and OR'd with the others. - private int mDownstreamTypesMask = DOWNSTREAM_NONE; - private boolean mNoUpstream = false; - private boolean mRoaming = false; - - // WARNING : this value is not able to being initialized to 0 and must have volatile because - // telephony service is not guaranteed that is up before tethering service starts. If telephony - // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid - // subscription id(0) to query resources. Therefore, initialized subscription id must be - // INVALID_SUBSCRIPTION_ID. - private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - RESTRICTED_NOTIFICATION_ID, - NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID - }) - @interface NotificationId {} - - private static final class MccMncOverrideInfo { - public final String visitedMccMnc; - public final int homeMcc; - public final int homeMnc; - MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) { - this.visitedMccMnc = visitedMccMnc; - this.homeMcc = mcc; - this.homeMnc = mnc; - } - } - - private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>(); - - static { - sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480)); - } - - public TetheringNotificationUpdater(@NonNull final Context context, - @NonNull final Looper looper) { - mContext = context; - mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) - .getSystemService(Context.NOTIFICATION_SERVICE); - mChannel = new NotificationChannel( - CHANNEL_ID, - context.getResources().getString(R.string.notification_channel_tethering_status), - NotificationManager.IMPORTANCE_LOW); - mNotificationManager.createNotificationChannel(mChannel); - mHandler = new NotificationHandler(looper); - } - - private class NotificationHandler extends Handler { - NotificationHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case EVENT_SHOW_NO_UPSTREAM: - notifyTetheringNoUpstream(); - break; - } - } - } - - /** Called when downstream has changed */ - public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { - updateActiveNotifications( - mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming); - } - - /** Called when active data subscription id changed */ - public void onActiveDataSubscriptionIdChanged(final int subId) { - updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming); - } - - /** Called when upstream network capabilities changed */ - public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) { - final boolean isNoUpstream = (capabilities == null); - final boolean isRoaming = capabilities != null - && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING); - updateActiveNotifications( - mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming); - } - - @NonNull - @VisibleForTesting - final Handler getHandler() { - return mHandler; - } - - @NonNull - @VisibleForTesting - Resources getResourcesForSubId(@NonNull final Context context, final int subId) { - final Resources res = SubscriptionManager.getResourcesForSubId(context, subId); - final TelephonyManager tm = - ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)) - .createForSubscriptionId(mActiveDataSubId); - final int carrierId = tm.getSimCarrierId(); - final String mccmnc = tm.getSimOperator(); - final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId); - if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) { - // Re-configure MCC/MNC value to specific carrier to get right resources. - final Configuration config = res.getConfiguration(); - config.mcc = overrideInfo.homeMcc; - config.mnc = overrideInfo.homeMnc; - return context.createConfigurationContext(config).getResources(); - } - return res; - } - - private void updateActiveNotifications(final int subId, final int downstreamTypes, - final boolean noUpstream, final boolean isRoaming) { - final boolean tetheringActiveChanged = - (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE); - final boolean subIdChanged = subId != mActiveDataSubId; - final boolean upstreamChanged = noUpstream != mNoUpstream; - final boolean roamingChanged = isRoaming != mRoaming; - final boolean updateAll = tetheringActiveChanged || subIdChanged; - mActiveDataSubId = subId; - mDownstreamTypesMask = downstreamTypes; - mNoUpstream = noUpstream; - mRoaming = isRoaming; - - if (updateAll || upstreamChanged) updateNoUpstreamNotification(); - if (updateAll || roamingChanged) updateRoamingNotification(); - } - - private void updateNoUpstreamNotification() { - final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - - if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) { - clearNotification(NO_UPSTREAM_NOTIFICATION_ID); - mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM); - } - } - - private void updateRoamingNotification() { - final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - - if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) { - clearNotification(ROAMING_NOTIFICATION_ID); - } - } - - @VisibleForTesting - void tetheringRestrictionLifted() { - clearNotification(RESTRICTED_NOTIFICATION_ID); - } - - private void clearNotification(@NotificationId final int id) { - mNotificationManager.cancel(null /* tag */, id); - } - - @VisibleForTesting - static String getSettingsPackageName(@NonNull final PackageManager pm) { - final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); - final ComponentName settingsComponent = settingsIntent.resolveActivity(pm); - return settingsComponent != null - ? settingsComponent.getPackageName() : "com.android.settings"; - } - - @VisibleForTesting - void notifyTetheringDisabledByRestriction() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final String title = res.getString(R.string.disable_tether_notification_title); - final String message = res.getString(R.string.disable_tether_notification_message); - if (isEmpty(title) || isEmpty(message)) return; - - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(mContext.getPackageManager())) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - PendingIntent.FLAG_IMMUTABLE, - null /* options */); - - showNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]); - } - - private void notifyTetheringNoUpstream() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final String title = res.getString(R.string.no_upstream_notification_title); - final String message = res.getString(R.string.no_upstream_notification_message); - final String disableButton = - res.getString(R.string.no_upstream_notification_disable_button); - if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return; - - final Intent intent = new Intent(ACTION_DISABLE_TETHERING); - intent.setPackage(mContext.getPackageName()); - final PendingIntent pi = PendingIntent.getBroadcast( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - intent, - PendingIntent.FLAG_IMMUTABLE); - final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); - - showNotification(R.drawable.stat_sys_tether_general, title, message, - NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action); - } - - private boolean setupRoamingNotification() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final boolean upstreamRoamingNotification = - res.getBoolean(R.bool.config_upstream_roaming_notification); - - if (!upstreamRoamingNotification) return NO_NOTIFY; - - final String title = res.getString(R.string.upstream_roaming_notification_title); - final String message = res.getString(R.string.upstream_roaming_notification_message); - if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; - - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(mContext.getPackageManager())) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - PendingIntent.FLAG_IMMUTABLE, - null /* options */); - - showNotification(R.drawable.stat_sys_tether_general, title, message, - ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]); - return NOTIFY_DONE; - } - - private boolean setupNoUpstreamNotification() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final int delayToShowUpstreamNotification = - res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul); - - if (delayToShowUpstreamNotification < 0) return NO_NOTIFY; - - mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM), - delayToShowUpstreamNotification); - return NOTIFY_DONE; - } - - private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message, @NotificationId final int id, final boolean ongoing, - @Nullable PendingIntent pi, @NonNull final Action... actions) { - final Notification notification = - new Notification.Builder(mContext, mChannel.getId()) - .setSmallIcon(iconId) - .setContentTitle(title) - .setContentText(message) - .setOngoing(ongoing) - .setColor(mContext.getColor( - android.R.color.system_notification_accent_color)) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setCategory(Notification.CATEGORY_STATUS) - .setContentIntent(pi) - .setActions(actions) - .build(); - - mNotificationManager.notify(null /* tag */, id, notification); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java deleted file mode 100644 index d637ba7557fa..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.Manifest.permission.ACCESS_NETWORK_STATE; -import static android.Manifest.permission.NETWORK_STACK; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED; -import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; - -import android.app.Service; -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.content.Intent; -import android.net.IIntResultListener; -import android.net.INetworkStackConnector; -import android.net.ITetheringConnector; -import android.net.ITetheringEventCallback; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.NetworkStack; -import android.net.TetheringRequestParcel; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.ip.IpServer; -import android.os.Binder; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.provider.Settings; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * Android service used to manage tethering. - * - * <p>The service returns a binder for the system server to communicate with the tethering. - */ -public class TetheringService extends Service { - private static final String TAG = TetheringService.class.getSimpleName(); - - private TetheringConnector mConnector; - - @Override - public void onCreate() { - final TetheringDependencies deps = makeTetheringDependencies(); - // The Tethering object needs a fully functional context to start, so this can't be done - // in the constructor. - mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this); - } - - /** - * Make a reference to Tethering object. - */ - @VisibleForTesting - public Tethering makeTethering(TetheringDependencies deps) { - System.loadLibrary("tetherutilsjni"); - return new Tethering(deps); - } - - @NonNull - @Override - public IBinder onBind(Intent intent) { - return mConnector; - } - - private static class TetheringConnector extends ITetheringConnector.Stub { - private final TetheringService mService; - private final Tethering mTethering; - - TetheringConnector(Tethering tether, TetheringService service) { - mTethering = tether; - mService = service; - } - - @Override - public void tether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(mTethering.tether(iface)); - } catch (RemoteException e) { } - } - - @Override - public void untether(String iface, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(mTethering.untether(iface)); - } catch (RemoteException e) { } - } - - @Override - public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(mTethering.setUsbTethering(enable)); - } catch (RemoteException e) { } - } - - @Override - public void startTethering(TetheringRequestParcel request, String callerPkg, - String callingAttributionTag, IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, - callingAttributionTag, - request.exemptFromEntitlementCheck /* onlyAllowPrivileged */, - listener)) { - return; - } - - mTethering.startTethering(request, listener); - } - - @Override - public void stopTethering(int type, String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - mTethering.stopTethering(type); - listener.onResult(TETHER_ERROR_NO_ERROR); - } catch (RemoteException e) { } - } - - @Override - public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, - boolean showEntitlementUi, String callerPkg, String callingAttributionTag) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, receiver)) return; - - mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); - } - - @Override - public void registerTetheringEventCallback(ITetheringEventCallback callback, - String callerPkg) { - try { - if (!hasTetherAccessPermission()) { - callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - return; - } - mTethering.registerTetheringEventCallback(callback); - } catch (RemoteException e) { } - } - - @Override - public void unregisterTetheringEventCallback(ITetheringEventCallback callback, - String callerPkg) { - try { - if (!hasTetherAccessPermission()) { - callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - return; - } - mTethering.unregisterTetheringEventCallback(callback); - } catch (RemoteException e) { } - } - - @Override - public void stopAllTethering(String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - mTethering.untetherAll(); - listener.onResult(TETHER_ERROR_NO_ERROR); - } catch (RemoteException e) { } - } - - @Override - public void isTetheringSupported(String callerPkg, String callingAttributionTag, - IIntResultListener listener) { - if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - - try { - listener.onResult(TETHER_ERROR_NO_ERROR); - } catch (RemoteException e) { } - } - - @Override - protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, - @Nullable String[] args) { - mTethering.dump(fd, writer, args); - } - - private boolean checkAndNotifyCommonError(final String callerPkg, - final String callingAttributionTag, final IIntResultListener listener) { - return checkAndNotifyCommonError(callerPkg, callingAttributionTag, - false /* onlyAllowPrivileged */, listener); - } - - private boolean checkAndNotifyCommonError(final String callerPkg, - final String callingAttributionTag, final boolean onlyAllowPrivileged, - final IIntResultListener listener) { - try { - if (!hasTetherChangePermission(callerPkg, callingAttributionTag, - onlyAllowPrivileged)) { - listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - return true; - } - if (!mTethering.isTetheringSupported()) { - listener.onResult(TETHER_ERROR_UNSUPPORTED); - return true; - } - } catch (RemoteException e) { - return true; - } - - return false; - } - - private boolean checkAndNotifyCommonError(final String callerPkg, - final String callingAttributionTag, final ResultReceiver receiver) { - if (!hasTetherChangePermission(callerPkg, callingAttributionTag, - false /* onlyAllowPrivileged */)) { - receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); - return true; - } - if (!mTethering.isTetheringSupported()) { - receiver.send(TETHER_ERROR_UNSUPPORTED, null); - return true; - } - - return false; - } - - private boolean hasNetworkStackPermission() { - return checkCallingOrSelfPermission(NETWORK_STACK) - || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK); - } - - private boolean hasTetherPrivilegedPermission() { - return checkCallingOrSelfPermission(TETHER_PRIVILEGED); - } - - private boolean checkCallingOrSelfPermission(final String permission) { - return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; - } - - private boolean hasTetherChangePermission(final String callerPkg, - final String callingAttributionTag, final boolean onlyAllowPrivileged) { - if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false; - - if (hasTetherPrivilegedPermission()) return true; - - if (mTethering.isTetherProvisioningRequired()) return false; - - int uid = Binder.getCallingUid(); - - // If callerPkg's uid is not same as Binder.getCallingUid(), - // checkAndNoteWriteSettingsOperation will return false and the operation will be - // denied. - return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, - callingAttributionTag, false /* throwException */); - } - - private boolean hasTetherAccessPermission() { - if (hasTetherPrivilegedPermission()) return true; - - return mService.checkCallingOrSelfPermission( - ACCESS_NETWORK_STATE) == PERMISSION_GRANTED; - } - } - - /** - * Check if the package is a allowed to write settings. This also accounts that such an access - * happened. - * - * @return {@code true} iff the package is allowed to write settings. - */ - @VisibleForTesting - boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, - @NonNull String callingPackage, @Nullable String callingAttributionTag, - boolean throwException) { - return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage, - callingAttributionTag, throwException); - } - - /** - * An injection method for testing. - */ - @VisibleForTesting - public TetheringDependencies makeTetheringDependencies() { - return new TetheringDependencies() { - @Override - public NetworkRequest getDefaultNetworkRequest() { - // TODO: b/147280869, add a proper system API to replace this. - final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .build(); - return trackDefaultRequest; - } - - @Override - public Looper getTetheringLooper() { - final HandlerThread tetherThread = new HandlerThread("android.tethering"); - tetherThread.start(); - return tetherThread.getLooper(); - } - - @Override - public Context getContext() { - return TetheringService.this; - } - - @Override - public IpServer.Dependencies getIpServerDependencies() { - return new IpServer.Dependencies() { - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { - try { - final INetworkStackConnector service = getNetworkStackConnector(); - if (service == null) return; - - service.makeDhcpServer(ifName, params, cb); - } catch (RemoteException e) { - Log.e(TAG, "Fail to make dhcp server"); - try { - cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); - } catch (RemoteException re) { } - } - } - }; - } - - // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring - // networkStackClient. - static final int NETWORKSTACK_TIMEOUT_MS = 60_000; - private INetworkStackConnector getNetworkStackConnector() { - IBinder connector; - try { - final long before = System.currentTimeMillis(); - while ((connector = NetworkStack.getService()) == null) { - if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); - return null; - } - Thread.sleep(200); - } - } catch (InterruptedException e) { - Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); - return null; - } - return INetworkStackConnector.Stub.asInterface(connector); - } - - @Override - public BluetoothAdapter getBluetoothAdapter() { - return BluetoothAdapter.getDefaultAdapter(); - } - }; - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java deleted file mode 100644 index b17065cb7804..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.ConnectivityManager.TYPE_BLUETOOTH; -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.IpPrefix; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.os.Handler; -import android.util.Log; -import android.util.SparseIntArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.StateMachine; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - - -/** - * A class to centralize all the network and link properties information - * pertaining to the current and any potential upstream network. - * - * The owner of UNM gets it to register network callbacks by calling the - * following methods : - * Calling #startTrackDefaultNetwork() to track the system default network. - * Calling #startObserveAllNetworks() to observe all networks. Listening all - * networks is necessary while the expression of preferred upstreams remains - * a list of legacy connectivity types. In future, this can be revisited. - * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network. - * - * The methods and data members of this class are only to be accessed and - * modified from the tethering main state machine thread. Any other - * access semantics would necessitate the addition of locking. - * - * TODO: Move upstream selection logic here. - * - * All callback methods are run on the same thread as the specified target - * state machine. This class does not require locking when accessed from this - * thread. Access from other threads is not advised. - * - * @hide - */ -public class UpstreamNetworkMonitor { - private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - public static final int EVENT_ON_CAPABILITIES = 1; - public static final int EVENT_ON_LINKPROPERTIES = 2; - public static final int EVENT_ON_LOST = 3; - public static final int NOTIFY_LOCAL_PREFIXES = 10; - // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default - // disabled. - @VisibleForTesting - public static final int TYPE_NONE = -1; - - private static final int CALLBACK_LISTEN_ALL = 1; - private static final int CALLBACK_DEFAULT_INTERNET = 2; - private static final int CALLBACK_MOBILE_REQUEST = 3; - - private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray(); - static { - sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR); - sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR); - sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR); - sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI); - sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH); - sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET); - } - - private final Context mContext; - private final SharedLog mLog; - private final StateMachine mTarget; - private final Handler mHandler; - private final int mWhat; - private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>(); - private HashSet<IpPrefix> mLocalPrefixes; - private ConnectivityManager mCM; - private EntitlementManager mEntitlementMgr; - private NetworkCallback mListenAllCallback; - private NetworkCallback mDefaultNetworkCallback; - private NetworkCallback mMobileNetworkCallback; - private boolean mDunRequired; - // Whether the current default upstream is mobile or not. - private boolean mIsDefaultCellularUpstream; - // The current system default network (not really used yet). - private Network mDefaultInternetNetwork; - // The current upstream network used for tethering. - private Network mTetheringUpstreamNetwork; - - public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) { - mContext = ctx; - mTarget = tgt; - mHandler = mTarget.getHandler(); - mLog = log.forSubComponent(TAG); - mWhat = what; - mLocalPrefixes = new HashSet<>(); - mIsDefaultCellularUpstream = false; - } - - @VisibleForTesting - public UpstreamNetworkMonitor( - ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) { - this((Context) null, tgt, log, what); - mCM = cm; - } - - /** - * Tracking the system default network. This method should be called when system is ready. - * - * @param defaultNetworkRequest should be the same as ConnectivityService default request - * @param entitle a EntitlementManager object to communicate between EntitlementManager and - * UpstreamNetworkMonitor - */ - public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest, - EntitlementManager entitle) { - - // defaultNetworkRequest is not really a "request", just a way of tracking the system - // default network. It's guaranteed not to actually bring up any networks because it's - // the should be the same request as the ConnectivityService default request, and thus - // shares fate with it. We can't use registerDefaultNetworkCallback because it will not - // track the system default network if there is a VPN that applies to our UID. - if (mDefaultNetworkCallback == null) { - mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET); - cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler); - } - if (mEntitlementMgr == null) { - mEntitlementMgr = entitle; - } - } - - /** Listen all networks. */ - public void startObserveAllNetworks() { - stop(); - - final NetworkRequest listenAllRequest = new NetworkRequest.Builder() - .clearCapabilities().build(); - mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL); - cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler); - } - - /** - * Stop tracking candidate tethering upstreams and release mobile network request. - * Note: this function is used when tethering is stopped because tethering do not need to - * choose upstream anymore. But it would not stop default network tracking because - * EntitlementManager may need to know default network to decide whether to request entitlement - * check even tethering is not active yet. - */ - public void stop() { - releaseMobileNetworkRequest(); - - releaseCallback(mListenAllCallback); - mListenAllCallback = null; - - mTetheringUpstreamNetwork = null; - mNetworkMap.clear(); - } - - /** Setup or teardown DUN connection according to |dunRequired|. */ - public void updateMobileRequiresDun(boolean dunRequired) { - final boolean valueChanged = (mDunRequired != dunRequired); - mDunRequired = dunRequired; - if (valueChanged && mobileNetworkRequested()) { - releaseMobileNetworkRequest(); - registerMobileNetworkRequest(); - } - } - - /** Whether mobile network is requested. */ - public boolean mobileNetworkRequested() { - return (mMobileNetworkCallback != null); - } - - /** Request mobile network if mobile upstream is permitted. */ - public void registerMobileNetworkRequest() { - if (!isCellularUpstreamPermitted()) { - mLog.i("registerMobileNetworkRequest() is not permitted"); - releaseMobileNetworkRequest(); - return; - } - if (mMobileNetworkCallback != null) { - mLog.e("registerMobileNetworkRequest() already registered"); - return; - } - - final NetworkRequest mobileUpstreamRequest; - if (mDunRequired) { - mobileUpstreamRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_DUN) - .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) - .addTransportType(TRANSPORT_CELLULAR).build(); - } else { - mobileUpstreamRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_INTERNET) - .addTransportType(TRANSPORT_CELLULAR).build(); - } - - // The existing default network and DUN callbacks will be notified. - // Therefore, to avoid duplicate notifications, we only register a no-op. - mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST); - - // The following use of the legacy type system cannot be removed until - // upstream selection no longer finds networks by legacy type. - // See also http://b/34364553 . - final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; - - // TODO: Change the timeout from 0 (no onUnavailable callback) to some - // moderate callback timeout. This might be useful for updating some UI. - // Additionally, we log a message to aid in any subsequent debugging. - mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest); - - cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler, - mMobileNetworkCallback); - } - - /** Release mobile network request. */ - public void releaseMobileNetworkRequest() { - if (mMobileNetworkCallback == null) return; - - cm().unregisterNetworkCallback(mMobileNetworkCallback); - mMobileNetworkCallback = null; - } - - // So many TODOs here, but chief among them is: make this functionality an - // integral part of this class such that whenever a higher priority network - // becomes available and useful we (a) file a request to keep it up as - // necessary and (b) change all upstream tracking state accordingly (by - // passing LinkProperties up to Tethering). - /** - * Select the first available network from |perferredTypes|. - */ - public UpstreamNetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) { - final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType( - mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted()); - - mLog.log("preferred upstream type: " + typeStatePair.type); - - switch (typeStatePair.type) { - case TYPE_MOBILE_DUN: - case TYPE_MOBILE_HIPRI: - // Tethering just selected mobile upstream in spite of the default network being - // not mobile. This can happen because of the priority list. - // Notify EntitlementManager to check permission for using mobile upstream. - if (!mIsDefaultCellularUpstream) { - mEntitlementMgr.maybeRunProvisioning(); - } - // If we're on DUN, put our own grab on it. - registerMobileNetworkRequest(); - break; - case TYPE_NONE: - // If we found NONE and mobile upstream is permitted we don't want to do this - // as we want any previous requests to keep trying to bring up something we can use. - if (!isCellularUpstreamPermitted()) releaseMobileNetworkRequest(); - break; - default: - // If we've found an active upstream connection that's not DUN/HIPRI - // we should stop any outstanding DUN/HIPRI requests. - releaseMobileNetworkRequest(); - break; - } - - return typeStatePair.ns; - } - - /** - * Get current preferred upstream network. If default network is cellular and DUN is required, - * preferred upstream would be DUN otherwise preferred upstream is the same as default network. - * Returns null if no current upstream is available. - */ - public UpstreamNetworkState getCurrentPreferredUpstream() { - final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null) - ? mNetworkMap.get(mDefaultInternetNetwork) - : null; - if (isNetworkUsableAndNotCellular(dfltState)) return dfltState; - - if (!isCellularUpstreamPermitted()) return null; - - if (!mDunRequired) return dfltState; - - // Find a DUN network. Note that code in Tethering causes a DUN request - // to be filed, but this might be moved into this class in future. - return findFirstDunNetwork(mNetworkMap.values()); - } - - /** Tell UpstreamNetworkMonitor which network is the current upstream of tethering. */ - public void setCurrentUpstream(Network upstream) { - mTetheringUpstreamNetwork = upstream; - } - - /** Return local prefixes. */ - public Set<IpPrefix> getLocalPrefixes() { - return (Set<IpPrefix>) mLocalPrefixes.clone(); - } - - private boolean isCellularUpstreamPermitted() { - if (mEntitlementMgr != null) { - return mEntitlementMgr.isCellularUpstreamPermitted(); - } else { - // This flow should only happens in testing. - return true; - } - } - - private void handleAvailable(Network network) { - if (mNetworkMap.containsKey(network)) return; - - if (VDBG) Log.d(TAG, "onAvailable for " + network); - mNetworkMap.put(network, new UpstreamNetworkState(null, null, network)); - } - - private void handleNetCap(Network network, NetworkCapabilities newNc) { - final UpstreamNetworkState prev = mNetworkMap.get(network); - if (prev == null || newNc.equals(prev.networkCapabilities)) { - // Ignore notifications about networks for which we have not yet - // received onAvailable() (should never happen) and any duplicate - // notifications (e.g. matching more than one of our callbacks). - return; - } - - if (VDBG) { - Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s", - network, newNc)); - } - - mNetworkMap.put(network, new UpstreamNetworkState( - prev.linkProperties, newNc, network)); - // TODO: If sufficient information is available to select a more - // preferable upstream, do so now and notify the target. - notifyTarget(EVENT_ON_CAPABILITIES, network); - } - - private void handleLinkProp(Network network, LinkProperties newLp) { - final UpstreamNetworkState prev = mNetworkMap.get(network); - if (prev == null || newLp.equals(prev.linkProperties)) { - // Ignore notifications about networks for which we have not yet - // received onAvailable() (should never happen) and any duplicate - // notifications (e.g. matching more than one of our callbacks). - return; - } - - if (VDBG) { - Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s", - network, newLp)); - } - - mNetworkMap.put(network, new UpstreamNetworkState( - newLp, prev.networkCapabilities, network)); - // TODO: If sufficient information is available to select a more - // preferable upstream, do so now and notify the target. - notifyTarget(EVENT_ON_LINKPROPERTIES, network); - } - - private void handleLost(Network network) { - // There are few TODOs within ConnectivityService's rematching code - // pertaining to spurious onLost() notifications. - // - // TODO: simplify this, probably if favor of code that: - // - selects a new upstream if mTetheringUpstreamNetwork has - // been lost (by any callback) - // - deletes the entry from the map only when the LISTEN_ALL - // callback gets notified. - - if (!mNetworkMap.containsKey(network)) { - // Ignore loss of networks about which we had not previously - // learned any information or for which we have already processed - // an onLost() notification. - return; - } - - if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network); - - // TODO: If sufficient information is available to select a more - // preferable upstream, do so now and notify the target. Likewise, - // if the current upstream network is gone, notify the target of the - // fact that we now have no upstream at all. - notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network)); - } - - private void recomputeLocalPrefixes() { - final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values()); - if (!mLocalPrefixes.equals(localPrefixes)) { - mLocalPrefixes = localPrefixes; - notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone()); - } - } - - // Fetch (and cache) a ConnectivityManager only if and when we need one. - private ConnectivityManager cm() { - if (mCM == null) { - // MUST call the String variant to be able to write unittests. - mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - } - return mCM; - } - - /** - * A NetworkCallback class that handles information of interest directly - * in the thread on which it is invoked. To avoid locking, this MUST be - * run on the same thread as the target state machine's handler. - */ - private class UpstreamNetworkCallback extends NetworkCallback { - private final int mCallbackType; - - UpstreamNetworkCallback(int callbackType) { - mCallbackType = callbackType; - } - - @Override - public void onAvailable(Network network) { - handleAvailable(network); - } - - @Override - public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) { - if (mCallbackType == CALLBACK_DEFAULT_INTERNET) { - mDefaultInternetNetwork = network; - final boolean newIsCellular = isCellular(newNc); - if (mIsDefaultCellularUpstream != newIsCellular) { - mIsDefaultCellularUpstream = newIsCellular; - mEntitlementMgr.notifyUpstream(newIsCellular); - } - return; - } - - handleNetCap(network, newNc); - } - - @Override - public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { - if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return; - - handleLinkProp(network, newLp); - // Any non-LISTEN_ALL callback will necessarily concern a network that will - // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback. - // So it's not useful to do this work for non-LISTEN_ALL callbacks. - if (mCallbackType == CALLBACK_LISTEN_ALL) { - recomputeLocalPrefixes(); - } - } - - @Override - public void onLost(Network network) { - if (mCallbackType == CALLBACK_DEFAULT_INTERNET) { - mDefaultInternetNetwork = null; - mIsDefaultCellularUpstream = false; - mEntitlementMgr.notifyUpstream(false); - return; - } - - handleLost(network); - // Any non-LISTEN_ALL callback will necessarily concern a network that will - // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback. - // So it's not useful to do this work for non-LISTEN_ALL callbacks. - if (mCallbackType == CALLBACK_LISTEN_ALL) { - recomputeLocalPrefixes(); - } - } - } - - private void releaseCallback(NetworkCallback cb) { - if (cb != null) cm().unregisterNetworkCallback(cb); - } - - private void notifyTarget(int which, Network network) { - notifyTarget(which, mNetworkMap.get(network)); - } - - private void notifyTarget(int which, Object obj) { - mTarget.sendMessage(mWhat, which, 0, obj); - } - - private static class TypeStatePair { - public int type = TYPE_NONE; - public UpstreamNetworkState ns = null; - } - - private static TypeStatePair findFirstAvailableUpstreamByType( - Iterable<UpstreamNetworkState> netStates, Iterable<Integer> preferredTypes, - boolean isCellularUpstreamPermitted) { - final TypeStatePair result = new TypeStatePair(); - - for (int type : preferredTypes) { - NetworkCapabilities nc; - try { - nc = networkCapabilitiesForType(type); - } catch (IllegalArgumentException iae) { - Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type); - continue; - } - if (!isCellularUpstreamPermitted && isCellular(nc)) { - continue; - } - - for (UpstreamNetworkState value : netStates) { - if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) { - continue; - } - - result.type = type; - result.ns = value; - return result; - } - } - - return result; - } - - private static HashSet<IpPrefix> allLocalPrefixes(Iterable<UpstreamNetworkState> netStates) { - final HashSet<IpPrefix> prefixSet = new HashSet<>(); - - for (UpstreamNetworkState ns : netStates) { - final LinkProperties lp = ns.linkProperties; - if (lp == null) continue; - prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp)); - } - - return prefixSet; - } - - private static boolean isCellular(UpstreamNetworkState ns) { - return (ns != null) && isCellular(ns.networkCapabilities); - } - - private static boolean isCellular(NetworkCapabilities nc) { - return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) - && nc.hasCapability(NET_CAPABILITY_NOT_VPN); - } - - private static boolean hasCapability(UpstreamNetworkState ns, int netCap) { - return (ns != null) && (ns.networkCapabilities != null) - && ns.networkCapabilities.hasCapability(netCap); - } - - private static boolean isNetworkUsableAndNotCellular(UpstreamNetworkState ns) { - return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) - && !isCellular(ns.networkCapabilities); - } - - private static UpstreamNetworkState findFirstDunNetwork( - Iterable<UpstreamNetworkState> netStates) { - for (UpstreamNetworkState ns : netStates) { - if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns; - } - - return null; - } - - /** - * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance. - * This function is used for deprecated legacy type and be disabled by default. - */ - @VisibleForTesting - public static NetworkCapabilities networkCapabilitiesForType(int type) { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); - - // Map from type to transports. - final int notFound = -1; - final int transport = sLegacyTypeToTransport.get(type, notFound); - if (transport == notFound) { - throw new IllegalArgumentException("unknown legacy type: " + type); - } - builder.addTransportType(transport); - - if (type == TYPE_MOBILE_DUN) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); - // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES. - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } else { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - return builder.build(); - } -} diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java deleted file mode 100644 index bab9f84cf762..000000000000 --- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; - -import androidx.annotation.NonNull; - -/** - * Snapshot of tethering upstream network state. - */ -public class UpstreamNetworkState { - /** {@link LinkProperties}. */ - public final LinkProperties linkProperties; - /** {@link NetworkCapabilities}. */ - public final NetworkCapabilities networkCapabilities; - /** {@link Network}. */ - public final Network network; - - /** Constructs a new UpstreamNetworkState. */ - public UpstreamNetworkState(LinkProperties linkProperties, - NetworkCapabilities networkCapabilities, Network network) { - this.linkProperties = linkProperties; - this.networkCapabilities = networkCapabilities; - this.network = network; - } - - @NonNull - @Override - public String toString() { - return String.format("UpstreamNetworkState{%s, %s, %s}", - network == null ? "null" : network, - networkCapabilities == null ? "null" : networkCapabilities, - linkProperties == null ? "null" : linkProperties); - } -} diff --git a/packages/Tethering/tests/Android.bp b/packages/Tethering/tests/Android.bp deleted file mode 100644 index cb0a20bdf0e8..000000000000 --- a/packages/Tethering/tests/Android.bp +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (C) 2020 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. -// - -filegroup { - name: "TetheringTestsJarJarRules", - srcs: ["jarjar-rules.txt"], - visibility: [ - "//frameworks/base/packages/Tethering/tests:__subpackages__", - ] -} diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp deleted file mode 100644 index 5765c01c43f3..000000000000 --- a/packages/Tethering/tests/integration/Android.bp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright (C) 2020 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. -// -java_defaults { - name: "TetheringIntegrationTestsDefaults", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - static_libs: [ - "NetworkStackApiStableLib", - "androidx.test.rules", - "mockito-target-extended-minus-junit4", - "net-tests-utils", - "testables", - ], - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - jarjar_rules: ":NetworkStackJarJarRules", -} - -android_library { - name: "TetheringIntegrationTestsLib", - platform_apis: true, - defaults: ["TetheringIntegrationTestsDefaults"], - visibility: ["//cts/tests/tests/tethering"] -} - -android_test { - name: "TetheringIntegrationTests", - platform_apis: true, - defaults: ["TetheringIntegrationTestsDefaults"], - test_suites: [ - "device-tests", - "mts", - ], - compile_multilib: "both", -} - -// Special version of the tethering tests that includes all tests necessary for code coverage -// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and -// NetworkStackTests. -android_test { - name: "TetheringCoverageTests", - platform_apis: true, - test_suites: ["device-tests", "mts"], - test_config: "AndroidTest_Coverage.xml", - defaults: ["libnetworkstackutilsjni_deps"], - static_libs: [ - "NetworkStaticLibTestsLib", - "NetworkStackTestsLib", - "TetheringTestsLib", - "TetheringIntegrationTestsLib", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - // For NetworkStackUtils included in NetworkStackBase - "libnetworkstackutilsjni", - ], - jarjar_rules: ":TetheringTestsJarJarRules", - compile_multilib: "both", - manifest: "AndroidManifest_coverage.xml", -} diff --git a/packages/Tethering/tests/integration/AndroidManifest.xml b/packages/Tethering/tests/integration/AndroidManifest.xml deleted file mode 100644 index fddfaad29f0f..000000000000 --- a/packages/Tethering/tests/integration/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.tethering.tests.integration"> - - <uses-permission android:name="android.permission.INTERNET"/> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.networkstack.tethering.tests.integration" - android:label="Tethering integration tests"> - </instrumentation> -</manifest> diff --git a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml deleted file mode 100644 index 06de00d78558..000000000000 --- a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.networkstack.tethering.tests.coverage"> - - <application tools:replace="android:label" - android:debuggable="true" - android:label="Tethering coverage tests"> - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.networkstack.tethering.tests.coverage" - android:label="Tethering coverage tests"> - </instrumentation> -</manifest> diff --git a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml deleted file mode 100644 index 3def2099e45f..000000000000 --- a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration description="Runs coverage tests for Tethering"> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> - <option name="test-file-name" value="TetheringCoverageTests.apk" /> - </target_preparer> - - <option name="test-tag" value="TetheringCoverageTests" /> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.networkstack.tethering.tests.coverage" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <option name="hidden-api-checks" value="false"/> - </test> -</configuration>
\ No newline at end of file diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java deleted file mode 100644 index d206ea0b4d45..000000000000 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import static android.Manifest.permission.MANAGE_TEST_NETWORKS; -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.net.TetheringManager.TETHERING_ETHERNET; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; - -import android.app.UiAutomation; -import android.content.Context; -import android.net.EthernetManager.TetheredInterfaceCallback; -import android.net.EthernetManager.TetheredInterfaceRequest; -import android.net.TetheringManager.StartTetheringCallback; -import android.net.TetheringManager.TetheringEventCallback; -import android.net.TetheringManager.TetheringRequest; -import android.net.dhcp.DhcpAckPacket; -import android.net.dhcp.DhcpOfferPacket; -import android.net.dhcp.DhcpPacket; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.system.Os; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.MediumTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.HandlerUtils; -import com.android.testutils.TapPacketReader; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.FileDescriptor; -import java.net.Inet4Address; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.List; -import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -@RunWith(AndroidJUnit4.class) -@MediumTest -public class EthernetTetheringTest { - - private static final String TAG = EthernetTetheringTest.class.getSimpleName(); - private static final int TIMEOUT_MS = 5000; - private static final int PACKET_READ_TIMEOUT_MS = 100; - private static final int DHCP_DISCOVER_ATTEMPTS = 10; - private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { - DhcpPacket.DHCP_SUBNET_MASK, - DhcpPacket.DHCP_ROUTER, - DhcpPacket.DHCP_DNS_SERVER, - DhcpPacket.DHCP_LEASE_TIME, - }; - private static final String DHCP_HOSTNAME = "testhostname"; - - private final Context mContext = InstrumentationRegistry.getContext(); - private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class); - private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class); - - private TestNetworkInterface mTestIface; - private HandlerThread mHandlerThread; - private Handler mHandler; - private TapPacketReader mTapPacketReader; - - private TetheredInterfaceRequester mTetheredInterfaceRequester; - private MyTetheringEventCallback mTetheringEventCallback; - - private UiAutomation mUiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - private boolean mRunTests; - - @Before - public void setUp() throws Exception { - // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive - // tethered client callbacks. - mUiAutomation.adoptShellPermissionIdentity( - MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED); - mRunTests = mTm.isTetheringSupported() && mEm != null; - assumeTrue(mRunTests); - - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); - } - - private void cleanUp() throws Exception { - mTm.stopTethering(TETHERING_ETHERNET); - if (mTetheringEventCallback != null) { - mTetheringEventCallback.awaitInterfaceUntethered(); - mTetheringEventCallback.unregister(); - mTetheringEventCallback = null; - } - if (mTapPacketReader != null) { - TapPacketReader reader = mTapPacketReader; - mHandler.post(() -> reader.stop()); - mTapPacketReader = null; - } - mHandlerThread.quitSafely(); - mTetheredInterfaceRequester.release(); - mEm.setIncludeTestInterfaces(false); - maybeDeleteTestInterface(); - } - - @After - public void tearDown() throws Exception { - try { - if (mRunTests) cleanUp(); - } finally { - mUiAutomation.dropShellPermissionIdentity(); - } - } - - @Test - public void testVirtualEthernetAlreadyExists() throws Exception { - // This test requires manipulating packets. Skip if there is a physical Ethernet connected. - assumeFalse(mEm.isAvailable()); - - mTestIface = createTestInterface(); - // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the - // interface will be placed in client mode, which will delete the link-local address. - // At that point NetworkInterface.getByName() will cease to work on the interface, because - // starting in R NetworkInterface can no longer see interfaces without IP addresses. - int mtu = getMTU(mTestIface); - - Log.d(TAG, "Including test interfaces"); - mEm.setIncludeTestInterfaces(true); - - final String iface = mTetheredInterfaceRequester.getInterface(); - assertEquals("TetheredInterfaceCallback for unexpected interface", - mTestIface.getInterfaceName(), iface); - - checkVirtualEthernet(mTestIface, mtu); - } - - @Test - public void testVirtualEthernet() throws Exception { - // This test requires manipulating packets. Skip if there is a physical Ethernet connected. - assumeFalse(mEm.isAvailable()); - - CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface(); - - mEm.setIncludeTestInterfaces(true); - - mTestIface = createTestInterface(); - - final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - assertEquals("TetheredInterfaceCallback for unexpected interface", - mTestIface.getInterfaceName(), iface); - - checkVirtualEthernet(mTestIface, getMTU(mTestIface)); - } - - @Test - public void testStaticIpv4() throws Exception { - assumeFalse(mEm.isAvailable()); - - mEm.setIncludeTestInterfaces(true); - - mTestIface = createTestInterface(); - - final String iface = mTetheredInterfaceRequester.getInterface(); - assertEquals("TetheredInterfaceCallback for unexpected interface", - mTestIface.getInterfaceName(), iface); - - assertInvalidStaticIpv4Request(iface, null, null); - assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); - assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); - assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); - assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); - assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); - assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); - - final String localAddr = "192.0.2.3/28"; - final String clientAddr = "192.0.2.2/28"; - mTetheringEventCallback = enableEthernetTethering(iface, - requestWithStaticIpv4(localAddr, clientAddr)); - - mTetheringEventCallback.awaitInterfaceTethered(); - assertInterfaceHasIpAddress(iface, localAddr); - - byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); - byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); - - FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); - mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); - DhcpResults dhcpResults = runDhcp(fd, client1); - assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); - - try { - runDhcp(fd, client2); - fail("Only one client should get an IP address"); - } catch (TimeoutException expected) { } - - } - - private boolean isAdbOverNetwork() { - // If adb TCP port opened, this test may running by adb over network. - return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) - || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); - } - - @Test - public void testPhysicalEthernet() throws Exception { - assumeTrue(mEm.isAvailable()); - // Do not run this test if adb is over network and ethernet is connected. - // It is likely the adb run over ethernet, the adb would break when ethernet is switching - // from client mode to server mode. See b/160389275. - assumeFalse(isAdbOverNetwork()); - - // Get an interface to use. - final String iface = mTetheredInterfaceRequester.getInterface(); - - // Enable Ethernet tethering and check that it starts. - mTetheringEventCallback = enableEthernetTethering(iface); - - // There is nothing more we can do on a physical interface without connecting an actual - // client, which is not possible in this test. - } - - private static final class MyTetheringEventCallback implements TetheringEventCallback { - private final TetheringManager mTm; - private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); - private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); - private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); - private final String mIface; - - private volatile boolean mInterfaceWasTethered = false; - private volatile boolean mUnregistered = false; - private volatile Collection<TetheredClient> mClients = null; - - MyTetheringEventCallback(TetheringManager tm, String iface) { - mTm = tm; - mIface = iface; - } - - public void unregister() { - mTm.unregisterTetheringEventCallback(this); - mUnregistered = true; - } - - @Override - public void onTetheredInterfacesChanged(List<String> interfaces) { - // Ignore stale callbacks registered by previous test cases. - if (mUnregistered) return; - - final boolean wasTethered = mTetheringStartedLatch.getCount() == 0; - if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) { - // This interface is being tethered for the first time. - Log.d(TAG, "Tethering started: " + interfaces); - mInterfaceWasTethered = true; - mTetheringStartedLatch.countDown(); - } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) { - Log.d(TAG, "Tethering stopped: " + interfaces); - mTetheringStoppedLatch.countDown(); - } - } - - public void awaitInterfaceTethered() throws Exception { - assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", - mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - public void awaitInterfaceUntethered() throws Exception { - // Don't block teardown if the interface was never tethered. - // This is racy because the interface might become tethered right after this check, but - // that can only happen in tearDown if startTethering timed out, which likely means - // the test has already failed. - if (!mInterfaceWasTethered) return; - - assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", - mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - @Override - public void onError(String ifName, int error) { - // Ignore stale callbacks registered by previous test cases. - if (mUnregistered) return; - - fail("TetheringEventCallback got error:" + error + " on iface " + ifName); - } - - @Override - public void onClientsChanged(Collection<TetheredClient> clients) { - // Ignore stale callbacks registered by previous test cases. - if (mUnregistered) return; - - Log.d(TAG, "Got clients changed: " + clients); - mClients = clients; - if (clients.size() > 0) { - mClientConnectedLatch.countDown(); - } - } - - public Collection<TetheredClient> awaitClientConnected() throws Exception { - assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms", - mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - return mClients; - } - } - - private MyTetheringEventCallback enableEthernetTethering(String iface, - TetheringRequest request) throws Exception { - MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); - mTm.registerTetheringEventCallback(mHandler::post, callback); - - StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { - @Override - public void onTetheringFailed(int resultCode) { - fail("Unexpectedly got onTetheringFailed"); - } - }; - Log.d(TAG, "Starting Ethernet tethering"); - mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); - callback.awaitInterfaceTethered(); - return callback; - } - - private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { - return enableEthernetTethering(iface, - new TetheringRequest.Builder(TETHERING_ETHERNET) - .setShouldShowEntitlementUi(false).build()); - } - - private int getMTU(TestNetworkInterface iface) throws SocketException { - NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); - assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); - return nif.getMTU(); - } - - private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { - final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); - mHandler.post(() -> reader.start()); - HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); - return reader; - } - - private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { - FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); - mTapPacketReader = makePacketReader(fd, mtu); - mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); - checkTetheredClientCallbacks(fd); - } - - private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception { - // We have to retransmit DHCP requests because IpServer declares itself to be ready before - // its DhcpServer is actually started. TODO: fix this race and remove this loop. - DhcpPacket offerPacket = null; - for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { - Log.d(TAG, "Sending DHCP discover"); - sendDhcpDiscover(fd, clientMacAddr); - offerPacket = getNextDhcpPacket(); - if (offerPacket instanceof DhcpOfferPacket) break; - } - if (!(offerPacket instanceof DhcpOfferPacket)) { - throw new TimeoutException("No DHCPOFFER received on interface within timeout"); - } - - sendDhcpRequest(fd, offerPacket, clientMacAddr); - DhcpPacket ackPacket = getNextDhcpPacket(); - if (!(ackPacket instanceof DhcpAckPacket)) { - throw new TimeoutException("No DHCPACK received on interface within timeout"); - } - - return ackPacket.toDhcpResults(); - } - - private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { - // Create a fake client. - byte[] clientMacAddr = new byte[6]; - new Random().nextBytes(clientMacAddr); - - DhcpResults dhcpResults = runDhcp(fd, clientMacAddr); - - final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected(); - assertEquals(1, clients.size()); - final TetheredClient client = clients.iterator().next(); - - // Check the MAC address. - assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); - assertEquals(TETHERING_ETHERNET, client.getTetheringType()); - - // Check the hostname. - assertEquals(1, client.getAddresses().size()); - TetheredClient.AddressInfo info = client.getAddresses().get(0); - assertEquals(DHCP_HOSTNAME, info.getHostname()); - - // Check the address is the one that was handed out in the DHCP ACK. - assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); - - // Check that the lifetime is correct +/- 10s. - final long now = SystemClock.elapsedRealtime(); - final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; - final String msg = String.format("IP address should have lifetime of %d, got %d", - dhcpResults.leaseDuration, actualLeaseDuration); - assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); - } - - private DhcpPacket getNextDhcpPacket() throws ParseException { - byte[] packet; - while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) { - try { - return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); - } catch (DhcpPacket.ParseException e) { - // Not a DHCP packet. Continue. - } - } - return null; - } - - private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { - private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1); - private final Handler mHandler; - private final EthernetManager mEm; - - private TetheredInterfaceRequest mRequest; - private final CompletableFuture<String> mFuture = new CompletableFuture<>(); - - TetheredInterfaceRequester(Handler handler, EthernetManager em) { - mHandler = handler; - mEm = em; - } - - @Override - public void onAvailable(String iface) { - Log.d(TAG, "Ethernet interface available: " + iface); - mFuture.complete(iface); - } - - @Override - public void onUnavailable() { - mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); - } - - public CompletableFuture<String> requestInterface() { - assertNull("BUG: more than one tethered interface request", mRequest); - Log.d(TAG, "Requesting tethered interface"); - mRequest = mEm.requestTetheredInterface(mHandler::post, this); - return mFuture; - } - - public String getInterface() throws Exception { - return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - public void release() { - if (mRequest != null) { - mFuture.obtrudeException(new IllegalStateException("Request already released")); - mRequest.release(); - mRequest = null; - } - } - } - - private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception { - ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, - new Random().nextInt() /* transactionId */, (short) 0 /* secs */, - macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, - false /* rapid commit */, DHCP_HOSTNAME); - sendPacket(fd, packet); - } - - private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress) - throws Exception { - DhcpResults results = offerPacket.toDhcpResults(); - Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); - Inet4Address serverIdentifier = results.serverAddress; - ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, - 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, - false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, - serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); - sendPacket(fd, packet); - } - - private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception { - assertNotNull("Only tests on virtual interfaces can send packets", fd); - Os.write(fd, packet); - } - - public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { - // Check all fields except the deprecation and expiry times. - String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); - assertTrue(msg, l1.isSameAddressAs(l2)); - assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); - assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); - } - - private TetheringRequest requestWithStaticIpv4(String local, String client) { - LinkAddress localAddr = local == null ? null : new LinkAddress(local); - LinkAddress clientAddr = client == null ? null : new LinkAddress(client); - return new TetheringRequest.Builder(TETHERING_ETHERNET) - .setStaticIpv4Addresses(localAddr, clientAddr) - .setShouldShowEntitlementUi(false).build(); - } - - private void assertInvalidStaticIpv4Request(String iface, String local, String client) - throws Exception { - try { - enableEthernetTethering(iface, requestWithStaticIpv4(local, client)); - fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); - } catch (IllegalArgumentException | NullPointerException expected) { } - } - - private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { - LinkAddress expectedAddr = new LinkAddress(expected); - NetworkInterface nif = NetworkInterface.getByName(iface); - for (InterfaceAddress ia : nif.getInterfaceAddresses()) { - final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); - if (expectedAddr.equals(addr)) { - return; - } - } - fail("Expected " + iface + " to have IP address " + expected + ", found " - + nif.getInterfaceAddresses()); - } - - private TestNetworkInterface createTestInterface() throws Exception { - TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); - TestNetworkInterface iface = tnm.createTapInterface(); - Log.d(TAG, "Created test interface " + iface.getInterfaceName()); - return iface; - } - - private void maybeDeleteTestInterface() throws Exception { - if (mTestIface != null) { - mTestIface.getFileDescriptor().close(); - Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName()); - mTestIface = null; - } - } -} diff --git a/packages/Tethering/tests/jarjar-rules.txt b/packages/Tethering/tests/jarjar-rules.txt deleted file mode 100644 index c99ff7f81877..000000000000 --- a/packages/Tethering/tests/jarjar-rules.txt +++ /dev/null @@ -1,19 +0,0 @@ -# Don't jar-jar the entire package because this test use some -# internal classes (like ArrayUtils in com.android.internal.util) -rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1 -rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1 -rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1 -rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1 -rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 -rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 -rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 - -rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 - -# Classes from net-utils-framework-common -rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1 - -# TODO: either stop using frameworks-base-testutils or remove the unit test classes it contains. -# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils. -zap android.os.test.TestLooperTest* -zap com.android.test.filters.SelectTestTests* diff --git a/packages/Tethering/tests/mts/Android.bp b/packages/Tethering/tests/mts/Android.bp deleted file mode 100644 index f925b0a53f32..000000000000 --- a/packages/Tethering/tests/mts/Android.bp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2020 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. - -android_test { - // This tests for functionality that is not required for devices that - // don't use Tethering mainline module. - name: "MtsTetheringTest", - - libs: [ - "android.test.base", - ], - - srcs: [ - "src/**/*.java", - ], - - static_libs: [ - "androidx.test.rules", - // mockito-target-extended-minus-junit4 used in this lib have dependency with - // jni_libs libdexmakerjvmtiagent and libstaticjvmtiagent. - "cts-net-utils", - // This is needed for androidx.test.runner.AndroidJUnitRunner. - "ctstestrunner-axt", - "junit", - "junit-params", - ], - - jni_libs: [ - // For mockito extended which is pulled in from -net-utils -> net-tests-utils - // (mockito-target-extended-minus-junit4). - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - - platform_apis: true, - - // Tag this module as a mts test artifact - test_suites: [ - "general-tests", - "mts", - ], - - // Include both the 32 and 64 bit versions - compile_multilib: "both", -} diff --git a/packages/Tethering/tests/mts/AndroidManifest.xml b/packages/Tethering/tests/mts/AndroidManifest.xml deleted file mode 100644 index 6d2abcad42a3..000000000000 --- a/packages/Tethering/tests/mts/AndroidManifest.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.tethering.mts"> - - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.INTERNET"/> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.tethering.mts" - android:label="MTS tests of android.tethering"> - <meta-data android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> - </instrumentation> - -</manifest> diff --git a/packages/Tethering/tests/mts/AndroidTest.xml b/packages/Tethering/tests/mts/AndroidTest.xml deleted file mode 100644 index 80788dfa6f40..000000000000 --- a/packages/Tethering/tests/mts/AndroidTest.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for MTS Tethering test cases"> - <option name="test-suite-tag" value="mts" /> - <option name="config-descriptor:metadata" key="component" value="networking" /> - <!-- Instant app do not have INTERNET permission. --> - <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> - <!-- Feature is not backed by native code. --> - <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> - <!-- Allow running this against a secondary user. --> - <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="MtsTetheringTest.apk" /> - </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="android.tethering.mts" /> - </test> - - <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> - <option name="mainline-module-package-name" value="com.google.android.tethering" /> - </object> -</configuration> diff --git a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java deleted file mode 100644 index 7ffe37ad648d..000000000000 --- a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.tethering.mts; - -import static android.Manifest.permission.MANAGE_TEST_NETWORKS; -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.Manifest.permission.READ_DEVICE_CONFIG; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.Manifest.permission.WRITE_SETTINGS; -import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; - -import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; - -import android.app.UiAutomation; -import android.content.Context; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.TetheringManager; -import android.net.cts.util.CtsTetheringUtils; -import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback; -import android.provider.DeviceConfig; - -import androidx.annotation.NonNull; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.TestNetworkTracker; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -public class TetheringModuleTest { - private Context mContext; - private TetheringManager mTm; - private CtsTetheringUtils mCtsTetheringUtils; - - private UiAutomation mUiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - - @Before - public void setUp() throws Exception { - mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, - WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED); - mContext = InstrumentationRegistry.getContext(); - mTm = mContext.getSystemService(TetheringManager.class); - mCtsTetheringUtils = new CtsTetheringUtils(mContext); - } - - @After - public void tearDown() throws Exception { - mUiAutomation.dropShellPermissionIdentity(); - } - - private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES = - "tether_enable_select_all_prefix_ranges"; - @Test - public void testSwitchBasePrefixRangeWhenConflict() throws Exception { - assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true)); - - addressConflictTest(true); - } - - @Test - public void testSwitchPrefixRangeWhenConflict() throws Exception { - addressConflictTest(false); - } - - private void addressConflictTest(final boolean wholeRangeConflict) throws Exception { - final TestTetheringEventCallback tetherEventCallback = - mCtsTetheringUtils.registerTetheringEventCallback(); - - TestNetworkTracker tnt = null; - try { - tetherEventCallback.assumeTetheringSupported(); - assumeTrue(isWifiTetheringSupported(tetherEventCallback)); - - mCtsTetheringUtils.startWifiTethering(tetherEventCallback); - - final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces(); - assertEquals(1, tetheredIfaces.size()); - final String wifiTetheringIface = tetheredIfaces.get(0); - - NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface); - // Tethering downstream only have one ipv4 address. - final LinkAddress hotspotAddr = getFirstIpv4Address(nif); - assertNotNull(hotspotAddr); - - final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict); - assertNotNull(testPrefix); - - tnt = setUpTestNetwork( - new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength())); - - tetherEventCallback.expectTetheredInterfacesChanged(null); - final List<String> wifiRegexs = - tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs(); - - tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs); - nif = NetworkInterface.getByName(wifiTetheringIface); - final LinkAddress newHotspotAddr = getFirstIpv4Address(nif); - assertNotNull(newHotspotAddr); - - assertFalse(testPrefix.containsPrefix( - new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength()))); - - mCtsTetheringUtils.stopWifiTethering(tetherEventCallback); - } finally { - if (tnt != null) { - tnt.teardown(); - } - mTm.stopAllTethering(); - mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback); - } - } - - private LinkAddress getFirstIpv4Address(final NetworkInterface nif) { - for (InterfaceAddress ia : nif.getInterfaceAddresses()) { - final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); - if (addr.isIpv4()) return addr; - } - return null; - } - - @NonNull - private IpPrefix getConflictingPrefix(final LinkAddress address, - final boolean wholeRangeConflict) { - if (!wholeRangeConflict) { - return new IpPrefix(address.getAddress(), address.getPrefixLength()); - } - - final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList( - new IpPrefix("192.168.0.0/16"), - new IpPrefix("172.16.0.0/12"), - new IpPrefix("10.0.0.0/8"))); - - for (IpPrefix prefix : prefixPool) { - if (prefix.contains(address.getAddress())) return prefix; - } - - fail("Could not find sutiable conflict prefix"); - - // Never go here. - return null; - } - - private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception { - return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/); - - } - - public static boolean isFeatureEnabled(final String name, final boolean defaultValue) { - return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue); - } -} diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp deleted file mode 100644 index 9217345dc2f5..000000000000 --- a/packages/Tethering/tests/privileged/Android.bp +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright (C) 2020 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. -// - -java_defaults { - name: "TetheringPrivilegedTestsJniDefaults", - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - "libtetherutilsjni", - ], - jni_uses_sdk_apis: true, - visibility: ["//visibility:private"], -} - -android_test { - name: "TetheringPrivilegedTests", - defaults: [ - "TetheringPrivilegedTestsJniDefaults", - ], - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - certificate: "networkstack", - platform_apis: true, - test_suites: [ - "device-tests", - "mts", - ], - static_libs: [ - "androidx.test.rules", - "net-tests-utils", - "TetheringApiCurrentLib", - ], - compile_multilib: "both", -} diff --git a/packages/Tethering/tests/privileged/AndroidManifest.xml b/packages/Tethering/tests/privileged/AndroidManifest.xml deleted file mode 100644 index 49eba15d13d4..000000000000 --- a/packages/Tethering/tests/privileged/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.tethering.tests.privileged" - android:sharedUserId="android.uid.networkstack"> - - <!-- Note: do not add any privileged or signature permissions that are granted - to the network stack and its shared uid apps. Otherwise, the test APK will - install, but when the device is rebooted, it will bootloop because this - test APK is not in the privileged permission allow list --> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.networkstack.tethering.tests.privileged" - android:label="Tethering privileged tests"> - </instrumentation> -</manifest> diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java deleted file mode 100644 index 42a91aa9acd5..000000000000 --- a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.system.OsConstants.IPPROTO_ICMPV6; - -import static com.android.net.module.util.IpUtils.icmpv6Checksum; -import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.app.Instrumentation; -import android.content.Context; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.MacAddress; -import android.net.util.InterfaceParams; -import android.net.util.TetheringUtils; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.TapPacketReader; -import com.android.testutils.TapPacketReaderRule; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; - -import java.nio.ByteBuffer; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DadProxyTest { - private static final int DATA_BUFFER_LEN = 4096; - private static final int PACKET_TIMEOUT_MS = 5_000; - - // Start the readers manually on a common handler shared with DadProxy, for simplicity - @Rule - public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule( - DATA_BUFFER_LEN, false /* autoStart */); - @Rule - public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule( - DATA_BUFFER_LEN, false /* autoStart */); - - private InterfaceParams mUpstreamParams, mTetheredParams; - private HandlerThread mHandlerThread; - private Handler mHandler; - private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader; - - private static INetd sNetd; - - @BeforeClass - public static void setupOnce() { - System.loadLibrary("tetherutilsjni"); - - final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); - final IBinder netdIBinder = - (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); - sNetd = INetd.Stub.asInterface(netdIBinder); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - - setupTapInterfaces(); - - // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. - if (Looper.myLooper() == null) Looper.prepare(); - - DadProxy mProxy = setupProxy(); - } - - @After - public void tearDown() throws Exception { - mUpstreamReader.stop(); - mTetheredReader.stop(); - - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - mHandlerThread.join(PACKET_TIMEOUT_MS); - } - - if (mTetheredParams != null) { - sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); - } - if (mUpstreamParams != null) { - sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); - } - } - - private void setupTapInterfaces() { - // Create upstream test iface. - mUpstreamReader.start(mHandler); - mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName()); - assertNotNull(mUpstreamParams); - mUpstreamPacketReader = mUpstreamReader.getReader(); - - // Create tethered test iface. - mTetheredReader.start(mHandler); - mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName()); - assertNotNull(mTetheredParams); - mTetheredPacketReader = mTetheredReader.getReader(); - } - - private static final int IPV6_HEADER_LEN = 40; - private static final int ETH_HEADER_LEN = 14; - private static final int ICMPV6_NA_NS_LEN = 24; - private static final int LL_TARGET_OPTION_LEN = 8; - private static final int ICMPV6_CHECKSUM_OFFSET = 2; - private static final int ETHER_TYPE_IPV6 = 0x86dd; - - private static ByteBuffer createDadPacket(int type) { - // Refer to buildArpPacket() - int icmpLen = ICMPV6_NA_NS_LEN - + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT - ? LL_TARGET_OPTION_LEN : 0); - final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN); - - // Ethernet header. - final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88"); - buf.put(srcMac.toByteArray()); - final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); - buf.put(dstMac.toByteArray()); - buf.putShort((short) ETHER_TYPE_IPV6); - - // IPv6 header - byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00}; - buf.put(version); // Version - buf.putShort((byte) icmpLen); // Length - buf.put((byte) IPPROTO_ICMPV6); // Next header - buf.put((byte) 0xff); // Hop limit - - final byte[] target = - InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress(); - final byte[] src; - final byte[] dst; - if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) { - src = InetAddresses.parseNumericAddress("::").getAddress(); - dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress(); - } else { - src = target; - dst = TetheringUtils.ALL_NODES; - } - buf.put(src); - buf.put(dst); - - // ICMPv6 Header - buf.put((byte) type); // Type - buf.put((byte) 0x00); // Code - buf.putShort((short) 0); // Checksum - buf.putInt(0); // Reserved - buf.put(target); - - if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) { - //NA packet has LL target address - //ICMPv6 Option - buf.put((byte) 0x02); // Type - buf.put((byte) 0x01); // Length - byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray(); - buf.put(ll_target); - } - - // Populate checksum field - final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN; - final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen); - buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum); - - buf.flip(); - return buf; - } - - private DadProxy setupProxy() throws Exception { - DadProxy proxy = new DadProxy(mHandler, mTetheredParams); - mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams)); - - // Upstream iface is added to local network to simplify test case. - // Otherwise the test needs to create and destroy a network for the upstream iface. - sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); - sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); - - return proxy; - } - - // TODO: change to assert. - private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) { - byte[] p; - - while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) { - final ByteBuffer buffer = ByteBuffer.wrap(p); - - if (buffer.compareTo(packet) == 0) return true; - } - return false; - } - - private void updateDstMac(ByteBuffer buf, MacAddress mac) { - buf.put(mac.toByteArray()); - buf.rewind(); - } - private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) { - buf.position(ETHER_SRC_ADDR_OFFSET); - buf.put(ifaceParams.macAddr.toByteArray()); - buf.rewind(); - } - - @Test - public void testNaForwardingFromUpstreamToTether() throws Exception { - ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); - - mUpstreamPacketReader.sendResponse(na); - updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01")); - updateSrcMac(na, mTetheredParams); - assertTrue(waitForPacket(na, mTetheredPacketReader)); - } - - @Test - // TODO: remove test once DAD works in both directions. - public void testNaForwardingFromTetherToUpstream() throws Exception { - ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); - - mTetheredPacketReader.sendResponse(na); - updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01")); - updateSrcMac(na, mTetheredParams); - assertFalse(waitForPacket(na, mUpstreamPacketReader)); - } - - @Test - public void testNsForwardingFromTetherToUpstream() throws Exception { - ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); - - mTetheredPacketReader.sendResponse(ns); - updateSrcMac(ns, mUpstreamParams); - assertTrue(waitForPacket(ns, mUpstreamPacketReader)); - } - - @Test - // TODO: remove test once DAD works in both directions. - public void testNsForwardingFromUpstreamToTether() throws Exception { - ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); - - mUpstreamPacketReader.sendResponse(ns); - updateSrcMac(ns, mUpstreamParams); - assertFalse(waitForPacket(ns, mTetheredPacketReader)); - } -} diff --git a/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java deleted file mode 100644 index 57c28fc67cc3..000000000000 --- a/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; -import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; -import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; - -import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_GET; -import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW; -import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK; -import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY; -import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.net.netlink.StructNlMsgHdr; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.NativeHandle; -import android.system.Os; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ConntrackSocketTest { - private static final long TIMEOUT = 500; - - private HandlerThread mHandlerThread; - private Handler mHandler; - private final SharedLog mLog = new SharedLog("privileged-test"); - - private OffloadHardwareInterface mOffloadHw; - private OffloadHardwareInterface.Dependencies mDeps; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - - // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. - if (Looper.myLooper() == null) Looper.prepare(); - - mDeps = new OffloadHardwareInterface.Dependencies(mLog); - mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps); - } - - @Test - public void testIpv4ConntrackSocket() throws Exception { - // Set up server and connect. - final InetSocketAddress anyAddress = new InetSocketAddress( - InetAddress.getByName("127.0.0.1"), 0); - final ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(anyAddress); - final SocketAddress theAddress = serverSocket.getLocalSocketAddress(); - - // Make a connection to the server. - final Socket socket = new Socket(); - socket.connect(theAddress); - final Socket acceptedSocket = serverSocket.accept(); - - final NativeHandle handle = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); - mOffloadHw.sendIpv4NfGenMsg(handle, - (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), - (short) (NLM_F_REQUEST | NLM_F_DUMP)); - - boolean foundConntrackEntry = false; - ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_RECV_BUFSIZE); - buffer.order(ByteOrder.nativeOrder()); - - try { - while (Os.read(handle.getFileDescriptor(), buffer) > 0) { - buffer.flip(); - - // TODO: ConntrackMessage should get a parse API like StructNlMsgHdr - // so we can confirm that the conntrack added is for the TCP connection above. - final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(buffer); - assertNotNull(nlmsghdr); - - // As long as 1 conntrack entry is found test case will pass, even if it's not - // the from the TCP connection above. - if (nlmsghdr.nlmsg_type == ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW)) { - foundConntrackEntry = true; - break; - } - } - } finally { - socket.close(); - serverSocket.close(); - } - assertTrue("Did not receive any NFNL_SUBSYS_CTNETLINK/IPCTNL_MSG_CT_NEW message", - foundConntrackEntry); - } -} diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp deleted file mode 100644 index 3589725dcf50..000000000000 --- a/packages/Tethering/tests/unit/Android.bp +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Tests in this folder are included both in unit tests and CTS. -java_library { - name: "TetheringCommonTests", - srcs: [ - "common/**/*.java", - "common/**/*.kt" - ], - static_libs: [ - "androidx.test.rules", - "net-tests-utils", - ], - // TODO(b/147200698) change sdk_version to module-current and remove framework-minus-apex - sdk_version: "core_platform", - libs: [ - "framework-minus-apex", - "framework-tethering.impl", - ], - visibility: ["//cts/tests/tests/tethering"], -} - -java_defaults { - name: "TetheringTestsDefaults", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - static_libs: [ - "TetheringApiCurrentLib", - "TetheringCommonTests", - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-extended-minus-junit4", - "net-tests-utils", - "testables", - ], - // TODO(b/147200698) change sdk_version to module-current and - // remove framework-minus-apex, ext, and framework-res - sdk_version: "core_platform", - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - "ext", - "framework-minus-apex", - "framework-res", - "framework-tethering.impl", - "framework-wifi.stubs.module_lib", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], -} - -// Library containing the unit tests. This is used by the coverage test target to pull in the -// unit test code. It is not currently used by the tests themselves because all the build -// configuration needed by the tests is in the TetheringTestsDefaults rule. -android_library { - name: "TetheringTestsLib", - defaults: ["TetheringTestsDefaults"], - visibility: [ - "//frameworks/base/packages/Tethering/tests/integration", - ] -} - -android_test { - name: "TetheringTests", - platform_apis: true, - test_suites: [ - "device-tests", - "mts", - ], - jarjar_rules: ":TetheringTestsJarJarRules", - defaults: ["TetheringTestsDefaults"], - compile_multilib: "both", -} diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml deleted file mode 100644 index 355342f64371..000000000000 --- a/packages/Tethering/tests/unit/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.tethering.tests.unit"> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - <service - android:name="com.android.networkstack.tethering.MockTetheringService" - android:permission="android.permission.TETHER_PRIVILEGED" - android:exported="true"> - <intent-filter> - <action android:name="com.android.networkstack.tethering.TetheringService"/> - </intent-filter> - </service> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.networkstack.tethering.tests.unit" - android:label="Tethering service tests"> - </instrumentation> -</manifest> diff --git a/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt deleted file mode 100644 index 55c59dd08f41..000000000000 --- a/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net - -import android.net.InetAddresses.parseNumericAddress -import android.net.TetheredClient.AddressInfo -import android.net.TetheringManager.TETHERING_BLUETOOTH -import android.net.TetheringManager.TETHERING_USB -import android.system.OsConstants.RT_SCOPE_UNIVERSE -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals - -private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67)) -private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78)) -private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L) -private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L) -private val TEST_HOSTNAME = "test_hostname" -private val TEST_OTHER_HOSTNAME = "test_other_hostname" -private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, TEST_HOSTNAME) -private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null) - -private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress( - parseNumericAddress(addr), - prefixLength, - 0 /* flags */, - RT_SCOPE_UNIVERSE, - expTime /* deprecationTime */, - expTime /* expirationTime */) - -@RunWith(AndroidJUnit4::class) -@SmallTest -class TetheredClientTest { - @Test - fun testParceling() { - assertParcelSane(TEST_ADDRINFO1, fieldCount = 2) - assertParcelSane(makeTestClient(), fieldCount = 3) - } - - @Test - fun testEquals() { - assertEquals(makeTestClient(), makeTestClient()) - - // Different mac address - assertNotEquals(makeTestClient(), TetheredClient( - TEST_OTHER_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Different hostname - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(AddressInfo(TEST_ADDR1, TEST_OTHER_HOSTNAME), TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Null hostname - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(AddressInfo(TEST_ADDR1, null), TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Missing address - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO2), - TETHERING_BLUETOOTH)) - - // Different type - assertNotEquals(makeTestClient(), TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_USB)) - } - - @Test - fun testAddAddresses() { - val client1 = TetheredClient(TEST_MACADDR, listOf(TEST_ADDRINFO1), TETHERING_USB) - val client2 = TetheredClient(TEST_OTHER_MACADDR, listOf(TEST_ADDRINFO2), TETHERING_USB) - assertEquals(TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_USB), client1.addAddresses(client2)) - } - - @Test - fun testGetters() { - assertEquals(TEST_MACADDR, makeTestClient().macAddress) - assertEquals(listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), makeTestClient().addresses) - assertEquals(TETHERING_BLUETOOTH, makeTestClient().tetheringType) - } - - @Test - fun testAddressInfo_Getters() { - assertEquals(TEST_ADDR1, TEST_ADDRINFO1.address) - assertEquals(TEST_ADDR2, TEST_ADDRINFO2.address) - assertEquals(TEST_HOSTNAME, TEST_ADDRINFO1.hostname) - assertEquals(null, TEST_ADDRINFO2.hostname) - } - - private fun makeTestClient() = TetheredClient( - TEST_MACADDR, - listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), - TETHERING_BLUETOOTH) -}
\ No newline at end of file diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java deleted file mode 100644 index a8857b2e5cb0..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.net.LinkAddress; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpServingParamsParcelExtTest { - private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123"); - private static final Inet4Address TEST_CLIENT_ADDRESS = inet4Addr("192.168.0.42"); - private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b; - private static final int TEST_CLIENT_ADDRESS_PARCELED = 0xc0a8002a; - private static final int TEST_PREFIX_LENGTH = 17; - private static final int TEST_LEASE_TIME_SECS = 120; - private static final int TEST_MTU = 1000; - private static final Set<Inet4Address> TEST_ADDRESS_SET = - new HashSet<Inet4Address>(Arrays.asList( - new Inet4Address[] {inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")})); - private static final Set<Integer> TEST_ADDRESS_SET_PARCELED = - new HashSet<Integer>(Arrays.asList(new Integer[] {0xc0a8017b, 0xc0a8017c})); - - private DhcpServingParamsParcelExt mParcel; - - @Before - public void setUp() { - mParcel = new DhcpServingParamsParcelExt(); - } - - @Test - public void testSetServerAddr() { - mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH)); - - assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr); - assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength); - } - - @Test - public void testSetDefaultRouters() { - mParcel.setDefaultRouters(TEST_ADDRESS_SET); - assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters)); - } - - @Test - public void testSetDnsServers() { - mParcel.setDnsServers(TEST_ADDRESS_SET); - assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers)); - } - - @Test - public void testSetExcludedAddrs() { - mParcel.setExcludedAddrs(TEST_ADDRESS_SET); - assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs)); - } - - @Test - public void testSetDhcpLeaseTimeSecs() { - mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS); - assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs); - } - - @Test - public void testSetLinkMtu() { - mParcel.setLinkMtu(TEST_MTU); - assertEquals(TEST_MTU, mParcel.linkMtu); - } - - @Test - public void testSetMetered() { - mParcel.setMetered(true); - assertTrue(mParcel.metered); - mParcel.setMetered(false); - assertFalse(mParcel.metered); - } - - @Test - public void testSetClientAddr() { - mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS); - assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.singleClientAddr); - } - - private static Inet4Address inet4Addr(String addr) { - return (Inet4Address) parseNumericAddress(addr); - } - - private static Set<Integer> asSet(int[] ints) { - return IntStream.of(ints).boxed().collect(Collectors.toSet()); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java deleted file mode 100644 index 2eb75895ac3e..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ /dev/null @@ -1,1201 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import static android.net.INetd.IF_STATE_UP; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_NCM; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.ip.IpServer.STATE_AVAILABLE; -import static android.net.ip.IpServer.STATE_LOCAL_ONLY; -import static android.net.ip.IpServer.STATE_TETHERED; -import static android.net.ip.IpServer.STATE_UNAVAILABLE; -import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; -import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH; -import static android.net.netlink.StructNdMsg.NUD_FAILED; -import static android.net.netlink.StructNdMsg.NUD_REACHABLE; -import static android.net.netlink.StructNdMsg.NUD_STALE; - -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.RouteInfo; -import android.net.TetherOffloadRuleParcel; -import android.net.TetherStatsParcel; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.IDhcpEventCallbacks; -import android.net.dhcp.IDhcpServer; -import android.net.dhcp.IDhcpServerCallbacks; -import android.net.ip.IpNeighborMonitor.NeighborEvent; -import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; -import android.net.ip.RouterAdvertisementDaemon.RaParams; -import android.net.util.InterfaceParams; -import android.net.util.InterfaceSet; -import android.net.util.PrefixUtils; -import android.net.util.SharedLog; -import android.os.Build; -import android.os.Handler; -import android.os.RemoteException; -import android.os.test.TestLooper; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.tethering.BpfCoordinator; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; -import com.android.networkstack.tethering.PrivateAddressCoordinator; -import com.android.networkstack.tethering.TetheringConfiguration; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.Captor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.Arrays; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpServerTest { - @Rule - public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); - - private static final String IFACE_NAME = "testnet1"; - private static final String UPSTREAM_IFACE = "upstream0"; - private static final String UPSTREAM_IFACE2 = "upstream1"; - private static final int UPSTREAM_IFINDEX = 101; - private static final int UPSTREAM_IFINDEX2 = 102; - private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; - private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; - private static final int DHCP_LEASE_TIME_SECS = 3600; - private static final boolean DEFAULT_USING_BPF_OFFLOAD = true; - - private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( - IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); - private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams( - UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); - private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams( - UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS, - 1500 /* defaultMtu */); - - private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; - - private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24"); - private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); - - @Mock private INetd mNetd; - @Mock private IpServer.Callback mCallback; - @Mock private SharedLog mSharedLog; - @Mock private IDhcpServer mDhcpServer; - @Mock private DadProxy mDadProxy; - @Mock private RouterAdvertisementDaemon mRaDaemon; - @Mock private IpNeighborMonitor mIpNeighborMonitor; - @Mock private IpServer.Dependencies mDependencies; - @Mock private PrivateAddressCoordinator mAddressCoordinator; - @Mock private NetworkStatsManager mStatsManager; - @Mock private TetheringConfiguration mTetherConfig; - - @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor; - - private final TestLooper mLooper = new TestLooper(); - private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor = - ArgumentCaptor.forClass(LinkProperties.class); - private IpServer mIpServer; - private InterfaceConfigurationParcel mInterfaceConfiguration; - private NeighborEventConsumer mNeighborEventConsumer; - private BpfCoordinator mBpfCoordinator; - - private void initStateMachine(int interfaceType) throws Exception { - initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD); - } - - private void initStateMachine(int interfaceType, boolean usingLegacyDhcp, - boolean usingBpfOffload) throws Exception { - when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy); - when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); - when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); - when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS); - when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2); - - when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX); - when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2); - - mInterfaceConfiguration = new InterfaceConfigurationParcel(); - mInterfaceConfiguration.flags = new String[0]; - if (interfaceType == TETHERING_BLUETOOTH) { - mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; - mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; - } - - ArgumentCaptor<NeighborEventConsumer> neighborCaptor = - ArgumentCaptor.forClass(NeighborEventConsumer.class); - doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(), - neighborCaptor.capture()); - - mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator, - mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies); - mIpServer.start(); - mNeighborEventConsumer = neighborCaptor.getValue(); - - // Starting the state machine always puts us in a consistent state and notifies - // the rest of the world that we've changed from an unknown to available state. - mLooper.dispatchAll(); - reset(mNetd, mCallback); - - when(mRaDaemon.start()).thenReturn(true); - } - - private void initTetheredStateMachine(int interfaceType, String upstreamIface) - throws Exception { - initTetheredStateMachine(interfaceType, upstreamIface, false, - DEFAULT_USING_BPF_OFFLOAD); - } - - private void initTetheredStateMachine(int interfaceType, String upstreamIface, - boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception { - initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - if (upstreamIface != null) { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(upstreamIface); - dispatchTetherConnectionChanged(upstreamIface, lp, 0); - } - reset(mNetd, mCallback, mAddressCoordinator); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn( - mTestAddress); - } - - private void setUpDhcpServer() throws Exception { - doAnswer(inv -> { - final IDhcpServerCallbacks cb = inv.getArgument(2); - new Thread(() -> { - try { - cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); - } catch (RemoteException e) { - fail(e.getMessage()); - } - }).run(); - return null; - }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); - } - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn( - mTestAddress); - when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); - - mBpfCoordinator = spy(new BpfCoordinator( - new BpfCoordinator.Dependencies() { - @NonNull - public Handler getHandler() { - return new Handler(mLooper.getLooper()); - } - - @NonNull - public INetd getNetd() { - return mNetd; - } - - @NonNull - public NetworkStatsManager getNetworkStatsManager() { - return mStatsManager; - } - - @NonNull - public SharedLog getSharedLog() { - return mSharedLog; - } - - @Nullable - public TetheringConfiguration getTetherConfig() { - return mTetherConfig; - } - })); - - setUpDhcpServer(); - } - - @Test - public void startsOutAvailable() { - when(mDependencies.getIpNeighborMonitor(any(), any(), any())) - .thenReturn(mIpNeighborMonitor); - mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, - mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies); - mIpServer.start(); - mLooper.dispatchAll(); - verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mCallback, mNetd); - } - - @Test - public void shouldDoNothingUntilRequested() throws Exception { - initStateMachine(TETHERING_BLUETOOTH); - final int [] noOp_commands = { - IpServer.CMD_TETHER_UNREQUESTED, - IpServer.CMD_IP_FORWARDING_ENABLE_ERROR, - IpServer.CMD_IP_FORWARDING_DISABLE_ERROR, - IpServer.CMD_START_TETHERING_ERROR, - IpServer.CMD_STOP_TETHERING_ERROR, - IpServer.CMD_SET_DNS_FORWARDERS_ERROR, - IpServer.CMD_TETHER_CONNECTION_CHANGED - }; - for (int command : noOp_commands) { - // None of these commands should trigger us to request action from - // the rest of the system. - dispatchCommand(command); - verifyNoMoreInteractions(mNetd, mCallback); - } - } - - @Test - public void handlesImmediateInterfaceDown() throws Exception { - initStateMachine(TETHERING_BLUETOOTH); - - dispatchCommand(IpServer.CMD_INTERFACE_DOWN); - verify(mCallback).updateInterfaceState( - mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); - verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void canBeTethered() throws Exception { - initStateMachine(TETHERING_BLUETOOTH); - - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd); - inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - // One for ipv4 route, one for ipv6 link local route. - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void canUnrequestTethering() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, null); - - dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mNetd).tetherApplyDnsInterfaces(); - inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); - inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - inOrder.verify(mAddressCoordinator).releaseDownstream(any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void canBeTetheredAsUsb() throws Exception { - initStateMachine(TETHERING_USB); - - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true)); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> - IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); - inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void canBeTetheredAsWifiP2p() throws Exception { - initStateMachine(TETHERING_WIFI_P2P); - - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true)); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> - IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP))); - inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void handlesFirstUpstreamChange() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, null); - - // Telling the state machine about its upstream interface triggers - // a little more configuration. - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void handlesChangingUpstream() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNetd, mCallback); - } - - @Test - public void handlesChangingUpstreamNatFailure() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - - doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); - } - - @Test - public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - - doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward( - IFACE_NAME, UPSTREAM_IFACE2); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); - } - - @Test - public void canUnrequestTetheringWithUpstream() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - - dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); - inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNetd).tetherApplyDnsInterfaces(); - inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); - inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - inOrder.verify(mAddressCoordinator).releaseDownstream(any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); - } - - @Test - public void interfaceDownLeadsToUnavailable() throws Exception { - for (boolean shouldThrow : new boolean[]{true, false}) { - initTetheredStateMachine(TETHERING_USB, null); - - if (shouldThrow) { - doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME); - } - dispatchCommand(IpServer.CMD_INTERFACE_DOWN); - InOrder usbTeardownOrder = inOrder(mNetd, mCallback); - // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down - // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address. - usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); - usbTeardownOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); - } - } - - @Test - public void usbShouldBeTornDownOnTetherError() throws Exception { - initStateMachine(TETHERING_USB); - - doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder usbTeardownOrder = inOrder(mNetd, mCallback); - usbTeardownOrder.verify(mNetd).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); - - usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); - usbTeardownOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); - } - - @Test - public void shouldTearDownUsbOnUpstreamError() throws Exception { - initTetheredStateMachine(TETHERING_USB, null); - - doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString()); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder usbTeardownOrder = inOrder(mNetd, mCallback); - usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); - - usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( - argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - usbTeardownOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR); - usbTeardownOrder.verify(mCallback).updateLinkProperties( - eq(mIpServer), mLinkPropertiesCaptor.capture()); - assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); - } - - @Test - public void ignoresDuplicateUpstreamNotifications() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - - verifyNoMoreInteractions(mNetd, mCallback); - - for (int i = 0; i < 5; i++) { - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mCallback); - } - } - - @Test - public void startsDhcpServer() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress)); - } - - @Test - public void startsDhcpServerOnBluetooth() throws Exception { - initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(mBluetoothPrefix); - } - - @Test - public void startsDhcpServerOnWifiP2p() throws Exception { - initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress)); - } - - @Test - public void startsDhcpServerOnNcm() throws Exception { - initStateMachine(TETHERING_NCM); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - assertDhcpStarted(new IpPrefix("192.168.42.0/24")); - } - - @Test - public void testOnNewPrefixRequest() throws Exception { - initStateMachine(TETHERING_NCM); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - - final IDhcpEventCallbacks eventCallbacks; - final ArgumentCaptor<IDhcpEventCallbacks> dhcpEventCbsCaptor = - ArgumentCaptor.forClass(IDhcpEventCallbacks.class); - verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), dhcpEventCbsCaptor.capture()); - eventCallbacks = dhcpEventCbsCaptor.getValue(); - assertDhcpStarted(new IpPrefix("192.168.42.0/24")); - - final ArgumentCaptor<LinkProperties> lpCaptor = - ArgumentCaptor.forClass(LinkProperties.class); - InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true)); - inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); - // One for ipv4 route, one for ipv6 link local route. - inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), - any(), any()); - inOrder.verify(mCallback).updateInterfaceState( - mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); - inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); - verifyNoMoreInteractions(mCallback, mAddressCoordinator); - - // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals - // onNewPrefixRequest callback. - final LinkAddress newAddress = new LinkAddress("192.168.100.125/24"); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn( - newAddress); - eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24")); - mLooper.dispatchAll(); - - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false)); - inOrder.verify(mNetd).tetherApplyDnsInterfaces(); - inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); - verifyNoMoreInteractions(mCallback); - - final LinkProperties linkProperties = lpCaptor.getValue(); - final List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses(); - assertEquals(1, linkProperties.getLinkAddresses().size()); - assertEquals(1, linkProperties.getRoutes().size()); - final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(), - linkAddresses.get(0).getPrefixLength()); - assertNotEquals(prefix, new IpPrefix("192.168.42.0/24")); - - verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any()); - assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix); - } - - @Test - public void doesNotStartDhcpServerIfDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - - verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); - } - - private InetAddress addr(String addr) throws Exception { - return InetAddresses.parseNumericAddress(addr); - } - - private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { - mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr, - nudState, mac)); - mLooper.dispatchAll(); - } - - private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { - mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr, - nudState, mac)); - mLooper.dispatchAll(); - } - - /** - * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable - * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as: - * - * private void checkFooCalled(StableParcelable p, ...) { - * ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class); - * verify(mMock).foo(captor.capture()); - * Foo foo = captor.getValue(); - * assertFooMatchesExpectations(foo); - * } - * - * almost works, but not quite. This is because if the code under test calls foo() twice, the - * first call to checkFooCalled() matches both the calls, putting both calls into the captor, - * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito - * features such as never(), inOrder(), etc. - * - * This approach isn't great because if the match fails, the error message is unhelpful - * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does - * work. - * - * TODO: consider making the error message more readable by adding a method that catching the - * AssertionFailedError and throwing a new assertion with more details. See - * NetworkMonitorTest#verifyNetworkTested. - * - * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the - * TooManyActualInvocations problem described above by forcing the caller of the custom assert - * method to specify all expected invocations in one call. This is useful when the stable - * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and - * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here - * because there is no such object. - */ - private static class TetherOffloadRuleParcelMatcher implements - ArgumentMatcher<TetherOffloadRuleParcel> { - public final int upstreamIfindex; - public final InetAddress dst; - public final MacAddress dstMac; - - TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) { - this.upstreamIfindex = upstreamIfindex; - this.dst = dst; - this.dstMac = dstMac; - } - - public boolean matches(TetherOffloadRuleParcel parcel) { - return upstreamIfindex == parcel.inputInterfaceIndex - && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex) - && Arrays.equals(dst.getAddress(), parcel.destination) - && (128 == parcel.prefixLength) - && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address) - && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); - } - - public String toString() { - return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s", - upstreamIfindex, dst.getHostAddress(), dstMac); - } - } - - @NonNull - private static TetherOffloadRuleParcel matches( - int upstreamIfindex, InetAddress dst, MacAddress dstMac) { - return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac)); - } - - @NonNull - private static Ipv6ForwardingRule makeForwardingRule( - int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) { - return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index, - (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac); - } - - @NonNull - private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) { - TetherStatsParcel parcel = new TetherStatsParcel(); - parcel.ifIndex = ifIndex; - return parcel; - } - - private void resetNetdAndBpfCoordinator() throws Exception { - reset(mNetd, mBpfCoordinator); - when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); - when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX)) - .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX)); - when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2)) - .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2)); - } - - @Test - public void addRemoveipv6ForwardingRules() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - DEFAULT_USING_BPF_OFFLOAD); - - final int myIfindex = TEST_IFACE_PARAMS.index; - final int notMyIfindex = myIfindex - 1; - - final MacAddress myMac = TEST_IFACE_PARAMS.macAddr; - final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1"); - final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2"); - final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1"); - final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234"); - final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); - final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); - final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b"); - - resetNetdAndBpfCoordinator(); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and - // tetherOffloadGetAndClearStats in netd while the rules are changed. - - // Events on other interfaces are ignored. - recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // Events on this interface are received and sent to netd. - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - resetNetdAndBpfCoordinator(); - - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - resetNetdAndBpfCoordinator(); - - // Link-local and multicast neighbors are ignored. - recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // A neighbor that is no longer valid causes the rule to be removed. - // NUD_FAILED events do not have a MAC address. - recvNewNeigh(myIfindex, neighA, NUD_FAILED, null); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull)); - resetNetdAndBpfCoordinator(); - - // A neighbor that is deleted causes the rule to be removed. - recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull)); - resetNetdAndBpfCoordinator(); - - // Upstream changes result in updating the rules. - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - resetNetdAndBpfCoordinator(); - - InOrder inOrder = inOrder(mNetd); - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(UPSTREAM_IFACE2); - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1); - verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); - resetNetdAndBpfCoordinator(); - - // When the upstream is lost, rules are removed. - dispatchTetherConnectionChanged(null, null, 0); - // Clear function is called two times by: - // - processMessage CMD_TETHER_CONNECTION_CHANGED for the upstream is lost. - // - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost. - // See dispatchTetherConnectionChanged. - verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); - resetNetdAndBpfCoordinator(); - - // If the upstream is IPv4-only, no rules are added. - dispatchTetherConnectionChanged(UPSTREAM_IFACE); - resetNetdAndBpfCoordinator(); - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - // Clear function is called by #updateIpv6ForwardingRules for the IPv6 upstream is lost. - verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); - verifyNoMoreInteractions(mBpfCoordinator, mNetd); - - // Rules can be added again once upstream IPv6 connectivity is available. - lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - - // If upstream IPv6 connectivity is lost, rules are removed. - resetNetdAndBpfCoordinator(); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - - // When the interface goes down, rules are removed. - lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); - resetNetdAndBpfCoordinator(); - - mIpServer.stop(); - mLooper.dispatchAll(); - verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); - verify(mIpNeighborMonitor).stop(); - resetNetdAndBpfCoordinator(); - } - - @Test - public void enableDisableUsingBpfOffload() throws Exception { - final int myIfindex = TEST_IFACE_PARAMS.index; - final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); - final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); - final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00"); - - // Expect that rules can be only added/removed when the BPF offload config is enabled. - // Note that the BPF offload disabled case is not a realistic test case. Because IP - // neighbor monitor doesn't start if BPF offload is disabled, there should have no - // neighbor event listening. This is used for testing the protection check just in case. - // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed - // anymore. - - // [1] Enable BPF offload. - // A neighbor that is added or deleted causes the rule to be added or removed. - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - true /* usingBpfOffload */); - resetNetdAndBpfCoordinator(); - - recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA)); - verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA)); - resetNetdAndBpfCoordinator(); - - recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull)); - verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull)); - resetNetdAndBpfCoordinator(); - - // [2] Disable BPF offload. - // A neighbor that is added or deleted doesn’t cause the rule to be added or removed. - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - false /* usingBpfOffload */); - resetNetdAndBpfCoordinator(); - - recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any()); - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - resetNetdAndBpfCoordinator(); - - recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any()); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - resetNetdAndBpfCoordinator(); - } - - @Test - public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */, - false /* usingBpfOffload */); - - // IP neighbor monitor doesn't start if BPF offload is disabled. - verify(mIpNeighborMonitor, never()).start(); - } - - private LinkProperties buildIpv6OnlyLinkProperties(final String iface) { - final LinkProperties linkProp = new LinkProperties(); - linkProp.setInterfaceName(iface); - linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64")); - linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST)); - final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888"); - linkProp.addDnsServer(dns); - - return linkProp; - } - - @Test - public void testAdjustTtlValue() throws Exception { - final ArgumentCaptor<RaParams> raParamsCaptor = - ArgumentCaptor.forClass(RaParams.class); - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams noV6Params = raParamsCaptor.getValue(); - assertEquals(65, noV6Params.hopLimit); - reset(mRaDaemon); - - when(mNetd.getProcSysNet( - INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64"); - final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams nonCellularParams = raParamsCaptor.getValue(); - assertEquals(65, nonCellularParams.hopLimit); - reset(mRaDaemon); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams noUpstream = raParamsCaptor.getValue(); - assertEquals(65, nonCellularParams.hopLimit); - reset(mRaDaemon); - - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); - verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); - final RaParams cellularParams = raParamsCaptor.getValue(); - assertEquals(63, cellularParams.hopLimit); - reset(mRaDaemon); - } - - @Test - public void testStopObsoleteDhcpServer() throws Exception { - final ArgumentCaptor<DhcpServerCallbacks> cbCaptor = - ArgumentCaptor.forClass(DhcpServerCallbacks.class); - doNothing().when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), - cbCaptor.capture()); - initStateMachine(TETHERING_WIFI); - dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - verify(mDhcpServer, never()).startWithCallbacks(any(), any()); - - // No stop dhcp server because dhcp server is not created yet. - dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - verify(mDhcpServer, never()).stop(any()); - - // Stop obsolete dhcp server. - try { - final DhcpServerCallbacks cb = cbCaptor.getValue(); - cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); - mLooper.dispatchAll(); - } catch (RemoteException e) { - fail(e.getMessage()); - } - verify(mDhcpServer).stop(any()); - } - - private void assertDhcpServingParams(final DhcpServingParamsParcel params, - final IpPrefix prefix) { - // Last address byte is random - assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr))); - assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength); - assertEquals(1, params.defaultRouters.length); - assertEquals(params.serverAddr, params.defaultRouters[0]); - assertEquals(1, params.dnsServers.length); - assertEquals(params.serverAddr, params.dnsServers[0]); - assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs); - if (mIpServer.interfaceType() == TETHERING_NCM) { - assertTrue(params.changePrefixOnDecline); - } - } - - private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { - verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); - verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix); - } - - /** - * Send a command to the state machine under test, and run the event loop to idle. - * - * @param command One of the IpServer.CMD_* constants. - * @param arg1 An additional argument to pass. - */ - private void dispatchCommand(int command, int arg1) { - mIpServer.sendMessage(command, arg1); - mLooper.dispatchAll(); - } - - /** - * Send a command to the state machine under test, and run the event loop to idle. - * - * @param command One of the IpServer.CMD_* constants. - */ - private void dispatchCommand(int command) { - mIpServer.sendMessage(command); - mLooper.dispatchAll(); - } - - /** - * Special override to tell the state machine that the upstream interface has changed. - * - * @see #dispatchCommand(int) - * @param upstreamIface String name of upstream interface (or null) - * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream. - */ - private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp, - int ttlAdjustment) { - dispatchTetherConnectionChanged(upstreamIface); - mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp); - mLooper.dispatchAll(); - } - - private void dispatchTetherConnectionChanged(String upstreamIface) { - final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null; - mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs); - mLooper.dispatchAll(); - } - - private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) { - // Find the first IPv4 LinkAddress. - LinkAddress addr4 = null; - for (LinkAddress addr : lp.getLinkAddresses()) { - if (!(addr.getAddress() instanceof Inet4Address)) continue; - addr4 = addr; - break; - } - assertNotNull("missing IPv4 address", addr4); - - final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength()); - // Assert the presence of the associated directly connected route. - final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(), - RouteInfo.RTN_UNICAST); - assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'", - lp.getRoutes().contains(directlyConnected)); - } - - private void assertNoAddressesNorRoutes(LinkProperties lp) { - assertTrue(lp.getLinkAddresses().isEmpty()); - assertTrue(lp.getRoutes().isEmpty()); - // We also check that interface name is non-empty, because we should - // never see an empty interface name in any LinkProperties update. - assertFalse(TextUtils.isEmpty(lp.getInterfaceName())); - } - - private boolean assertContainsFlag(String[] flags, String match) { - for (String flag : flags) { - if (flag.equals(match)) return true; - } - fail("Missing flag: " + match); - return false; - } - - private boolean assertNotContainsFlag(String[] flags, String match) { - for (String flag : flags) { - if (flag.equals(match)) { - fail("Unexpected flag: " + match); - return false; - } - } - return true; - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void dadProxyUpdates() throws Exception { - InOrder inOrder = inOrder(mDadProxy); - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Add an upstream without IPv6. - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); - inOrder.verify(mDadProxy).setUpstreamIface(null); - - // Add IPv6 to the upstream. - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Change upstream. - // New linkproperties is needed, otherwise changing the iface has no impact. - LinkProperties lp2 = new LinkProperties(); - lp2.setInterfaceName(UPSTREAM_IFACE2); - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2); - - // Lose IPv6 on the upstream... - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0); - inOrder.verify(mDadProxy).setUpstreamIface(null); - - // ... and regain it on a different upstream. - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Lose upstream. - dispatchTetherConnectionChanged(null, null, 0); - inOrder.verify(mDadProxy).setUpstreamIface(null); - - // Regain upstream. - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - - // Stop tethering. - mIpServer.stop(); - mLooper.dispatchAll(); - } - - private void checkDadProxyEnabled(boolean expectEnabled) throws Exception { - initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - InOrder inOrder = inOrder(mDadProxy); - // Add IPv6 to the upstream. - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(UPSTREAM_IFACE); - if (expectEnabled) { - inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); - } else { - inOrder.verifyNoMoreInteractions(); - } - // Stop tethering. - mIpServer.stop(); - mLooper.dispatchAll(); - if (expectEnabled) { - inOrder.verify(mDadProxy).stop(); - } - else { - verify(mDependencies, never()).getDadProxy(any(), any()); - } - } - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testDadProxyUpdates_DisabledUpToR() throws Exception { - checkDadProxyEnabled(false); - } - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testDadProxyUpdates_EnabledAfterR() throws Exception { - checkDadProxyEnabled(true); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java deleted file mode 100644 index ea084b607868..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class InterfaceSetTest { - @Test - public void testNullNamesIgnored() { - final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null); - assertEquals(2, set.ifnames.size()); - assertTrue(set.ifnames.contains("if1")); - assertTrue(set.ifnames.contains("if2")); - } - - @Test - public void testToString() { - final InterfaceSet set = new InterfaceSet("if1", "if2"); - final String setString = set.toString(); - assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]")); - } - - @Test - public void testToString_Empty() { - final InterfaceSet set = new InterfaceSet(null, null); - assertEquals("[]", set.toString()); - } - - @Test - public void testEquals() { - assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1")); - assertEquals(new InterfaceSet(null, null), new InterfaceSet()); - assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2"))); - assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1"))); - assertFalse(new InterfaceSet().equals(null)); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java deleted file mode 100644 index 91c7771cc7fe..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.util; - -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import android.net.LinkAddress; -import android.net.TetheringRequestParcel; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.MiscAsserts; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class TetheringUtilsTest { - private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24"); - private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24"); - private TetheringRequestParcel mTetheringRequest; - - @Before - public void setUp() { - mTetheringRequest = makeTetheringRequestParcel(); - } - - public TetheringRequestParcel makeTetheringRequestParcel() { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - request.localIPv4Address = TEST_SERVER_ADDR; - request.staticClientAddress = TEST_CLIENT_ADDR; - request.exemptFromEntitlementCheck = false; - request.showProvisioningUi = true; - return request; - } - - @Test - public void testIsTetheringRequestEquals() throws Exception { - TetheringRequestParcel request = makeTetheringRequestParcel(); - - assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest)); - assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - assertTrue(TetheringUtils.isTetheringRequestEquals(null, null)); - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null)); - assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest)); - - request = makeTetheringRequestParcel(); - request.tetheringType = TETHERING_USB; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - request = makeTetheringRequestParcel(); - request.localIPv4Address = null; - request.staticClientAddress = null; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - request = makeTetheringRequestParcel(); - request.exemptFromEntitlementCheck = true; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - request = makeTetheringRequestParcel(); - request.showProvisioningUi = false; - assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - - MiscAsserts.assertFieldCountEquals(5, TetheringRequestParcel.class); - } -} diff --git a/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java b/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java deleted file mode 100644 index 5a9b6e380ea9..000000000000 --- a/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.reset; - -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.BroadcastInterceptingContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class VersionedBroadcastListenerTest { - private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName(); - private static final String ACTION_TEST = "action.test.happy.broadcasts"; - - @Mock private Context mContext; - private BroadcastInterceptingContext mServiceContext; - private Handler mHandler; - private VersionedBroadcastListener mListener; - private int mCallbackCount; - - private void doCallback() { - mCallbackCount++; - } - - private class MockContext extends BroadcastInterceptingContext { - MockContext(Context base) { - super(base); - } - } - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - } - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - reset(mContext); - mServiceContext = new MockContext(mContext); - mHandler = new Handler(Looper.myLooper()); - mCallbackCount = 0; - final IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_TEST); - mListener = new VersionedBroadcastListener( - TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback()); - } - - @After public void tearDown() throws Exception { - if (mListener != null) { - mListener.stopListening(); - mListener = null; - } - } - - private void sendBroadcast() { - final Intent intent = new Intent(ACTION_TEST); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - @Test - public void testBasicListening() { - assertEquals(0, mCallbackCount); - mListener.startListening(); - for (int i = 0; i < 5; i++) { - sendBroadcast(); - assertEquals(i + 1, mCallbackCount); - } - mListener.stopListening(); - } - - @Test - public void testBroadcastsBeforeStartAreIgnored() { - assertEquals(0, mCallbackCount); - for (int i = 0; i < 5; i++) { - sendBroadcast(); - assertEquals(0, mCallbackCount); - } - - mListener.startListening(); - sendBroadcast(); - assertEquals(1, mCallbackCount); - } - - @Test - public void testBroadcastsAfterStopAreIgnored() { - mListener.startListening(); - sendBroadcast(); - assertEquals(1, mCallbackCount); - mListener.stopListening(); - - for (int i = 0; i < 5; i++) { - sendBroadcast(); - assertEquals(1, mCallbackCount); - } - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java deleted file mode 100644 index 64242ae8255f..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; - -import static com.android.networkstack.tethering.BpfCoordinator.StatsType; -import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; -import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.argThat; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.MacAddress; -import android.net.NetworkStats; -import android.net.TetherOffloadRuleParcel; -import android.net.TetherStatsParcel; -import android.net.ip.IpServer; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.test.TestLooper; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; -import com.android.testutils.TestableNetworkStatsProviderCbBinder; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class BpfCoordinatorTest { - private static final int DOWNSTREAM_IFINDEX = 10; - private static final MacAddress DOWNSTREAM_MAC = MacAddress.ALL_ZEROS_ADDRESS; - private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1"); - private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2"); - private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a"); - private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b"); - - @Mock private NetworkStatsManager mStatsManager; - @Mock private INetd mNetd; - @Mock private IpServer mIpServer; - @Mock private TetheringConfiguration mTetherConfig; - - // Late init since methods must be called by the thread that created this object. - private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; - private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider; - private final ArgumentCaptor<ArrayList> mStringArrayCaptor = - ArgumentCaptor.forClass(ArrayList.class); - private final TestLooper mTestLooper = new TestLooper(); - private BpfCoordinator.Dependencies mDeps = - new BpfCoordinator.Dependencies() { - @NonNull - public Handler getHandler() { - return new Handler(mTestLooper.getLooper()); - } - - @NonNull - public INetd getNetd() { - return mNetd; - } - - @NonNull - public NetworkStatsManager getNetworkStatsManager() { - return mStatsManager; - } - - @NonNull - public SharedLog getSharedLog() { - return new SharedLog("test"); - } - - @Nullable - public TetheringConfiguration getTetherConfig() { - return mTetherConfig; - } - }; - - @Before public void setUp() { - MockitoAnnotations.initMocks(this); - when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); - } - - private void waitForIdle() { - mTestLooper.dispatchAll(); - } - - private void setupFunctioningNetdInterface() throws Exception { - when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); - } - - @NonNull - private BpfCoordinator makeBpfCoordinator() throws Exception { - final BpfCoordinator coordinator = new BpfCoordinator(mDeps); - final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider> - tetherStatsProviderCaptor = - ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class); - verify(mStatsManager).registerNetworkStatsProvider(anyString(), - tetherStatsProviderCaptor.capture()); - mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); - assertNotNull(mTetherStatsProvider); - mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); - mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); - return coordinator; - } - - @NonNull - private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how, - @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { - return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, - SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, - rxPackets, txBytes, txPackets, 0L); - } - - @NonNull - private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex, - long rxBytes, long rxPackets, long txBytes, long txPackets) { - final TetherStatsParcel parcel = new TetherStatsParcel(); - parcel.ifIndex = ifIndex; - parcel.rxBytes = rxBytes; - parcel.rxPackets = rxPackets; - parcel.txBytes = txBytes; - parcel.txPackets = txPackets; - return parcel; - } - - // Set up specific tether stats list and wait for the stats cache is updated by polling thread - // in the coordinator. Beware of that it is only used for the default polling interval. - private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception { - when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - } - - @Test - public void testGetForwardedStats() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.startPolling(); - - final String wlanIface = "wlan0"; - final Integer wlanIfIndex = 100; - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 101; - - // Add interface name to lookup table. In realistic case, the upstream interface name will - // be added by IpServer when IpServer has received with a new IPv6 upstream update event. - coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface); - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // [1] Both interface stats are changed. - // Setup the tether stats of wlan and mobile interface. Note that move forward the time of - // the looper to make sure the new tether stats has been updated by polling update thread. - setTetherOffloadStatsList(new TetherStatsParcel[] { - buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), - buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)}); - - final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200)) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400)); - - final NetworkStats expectedUidStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200)) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400)); - - // Force pushing stats update to verify the stats reported. - // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for - // verifying the notification. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); - - // [2] Only one interface stats is changed. - // The tether stats of mobile interface is accumulated and The tether stats of wlan - // interface is the same. - setTetherOffloadStatsList(new TetherStatsParcel[] { - buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), - buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)}); - - final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40)); - - final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40)); - - // Force pushing stats update to verify that only diff of stats is reported. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, - expectedUidStatsDiff); - - // [3] Stop coordinator. - // Shutdown the coordinator and clear the invocation history, especially the - // tetherOffloadGetStats() calls. - coordinator.stopPolling(); - clearInvocations(mNetd); - - // Verify the polling update thread stopped. - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - verify(mNetd, never()).tetherOffloadGetStats(); - } - - @Test - public void testOnSetAlert() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.startPolling(); - - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 100; - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // Verify that set quota to 0 will immediately triggers a callback. - mTetherStatsProvider.onSetAlert(0); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that notifyAlertReached never fired if quota is not yet reached. - when(mNetd.tetherOffloadGetStats()).thenReturn( - new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)}); - mTetherStatsProvider.onSetAlert(100); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - - // Verify that notifyAlertReached fired when quota is reached. - when(mNetd.tetherOffloadGetStats()).thenReturn( - new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)}); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that set quota with UNLIMITED won't trigger any callback. - mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - } - - // The custom ArgumentMatcher simply comes from IpServerTest. - // TODO: move both of them into a common utility class for reusing the code. - private static class TetherOffloadRuleParcelMatcher implements - ArgumentMatcher<TetherOffloadRuleParcel> { - public final int upstreamIfindex; - public final int downstreamIfindex; - public final Inet6Address address; - public final MacAddress srcMac; - public final MacAddress dstMac; - - TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) { - upstreamIfindex = rule.upstreamIfindex; - downstreamIfindex = rule.downstreamIfindex; - address = rule.address; - srcMac = rule.srcMac; - dstMac = rule.dstMac; - } - - public boolean matches(@NonNull TetherOffloadRuleParcel parcel) { - return upstreamIfindex == parcel.inputInterfaceIndex - && (downstreamIfindex == parcel.outputInterfaceIndex) - && Arrays.equals(address.getAddress(), parcel.destination) - && (128 == parcel.prefixLength) - && Arrays.equals(srcMac.toByteArray(), parcel.srcL2Address) - && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); - } - - public String toString() { - return String.format("TetherOffloadRuleParcelMatcher(%d, %d, %s, %s, %s", - upstreamIfindex, downstreamIfindex, address.getHostAddress(), srcMac, dstMac); - } - } - - @NonNull - private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) { - return argThat(new TetherOffloadRuleParcelMatcher(rule)); - } - - @NonNull - private static Ipv6ForwardingRule buildTestForwardingRule( - int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) { - return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address, - DOWNSTREAM_MAC, dstMac); - } - - @Test - public void testSetDataLimit() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 100; - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // [1] Default limit. - // Set the unlimited quota as default if the service has never applied a data limit for a - // given upstream. Note that the data limit only be applied on an upstream which has rules. - final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); - final InOrder inOrder = inOrder(mNetd); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(rule)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED); - inOrder.verifyNoMoreInteractions(); - - // [2] Specific limit. - // Applying the data limit boundary {min, 1gb, max, infinity} on current upstream. - for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) { - mTetherStatsProvider.onSetLimit(mobileIface, quota); - waitForIdle(); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota); - inOrder.verifyNoMoreInteractions(); - } - - // [3] Invalid limit. - // The valid range of quota is 0..max_int64 or -1 (unlimited). - final long invalidLimit = Long.MIN_VALUE; - try { - mTetherStatsProvider.onSetLimit(mobileIface, invalidLimit); - waitForIdle(); - fail("No exception thrown for invalid limit " + invalidLimit + "."); - } catch (IllegalArgumentException expected) { - assertEquals(expected.getMessage(), "invalid quota value " + invalidLimit); - } - } - - // TODO: Test the case in which the rules are changed from different IpServer objects. - @Test - public void testSetDataLimitOnRuleChange() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - final String mobileIface = "rmnet_data0"; - final Integer mobileIfIndex = 100; - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - // Applying a data limit to the current upstream does not take any immediate action. - // The data limit could be only set on an upstream which has rules. - final long limit = 12345; - final InOrder inOrder = inOrder(mNetd); - mTetherStatsProvider.onSetLimit(mobileIface, limit); - waitForIdle(); - inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); - - // Adding the first rule on current upstream immediately sends the quota to netd. - final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleA)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, limit); - inOrder.verifyNoMoreInteractions(); - - // Adding the second rule on current upstream does not send the quota to netd. - final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleB)); - inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); - - // Removing the second rule on current upstream does not send the quota to netd. - coordinator.tetherOffloadRuleRemove(mIpServer, ruleB); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleB)); - inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); - - // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. - when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex)) - .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); - coordinator.tetherOffloadRuleRemove(mIpServer, ruleA); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleA)); - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testTetherOffloadRuleUpdateAndClear() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - final String ethIface = "eth1"; - final String mobileIface = "rmnet_data0"; - final Integer ethIfIndex = 100; - final Integer mobileIfIndex = 101; - coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface); - coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); - - final InOrder inOrder = inOrder(mNetd); - - // Before the rule test, here are the additional actions while the rules are changed. - // - After adding the first rule on a given upstream, the coordinator adds a data limit. - // If the service has never applied the data limit, set an unlimited quota as default. - // - After removing the last rule on a given upstream, the coordinator gets the last stats. - // Then, it clears the stats and the limit entry from BPF maps. - // See tetherOffloadRule{Add, Remove, Clear, Clean}. - - // [1] Adding rules on the upstream Ethernet. - // Note that the default data limit is applied after the first rule is added. - final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule( - ethIfIndex, NEIGH_A, MAC_A); - final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule( - ethIfIndex, NEIGH_B, MAC_B); - - coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleA)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(ethIfIndex, QUOTA_UNLIMITED); - - coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleB)); - - // [2] Update the existing rules from Ethernet to cellular. - final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule( - mobileIfIndex, NEIGH_A, MAC_A); - final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule( - mobileIfIndex, NEIGH_B, MAC_B); - when(mNetd.tetherOffloadGetAndClearStats(ethIfIndex)) - .thenReturn(buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40)); - - // Update the existing rules for upstream changes. The rules are removed and re-added one - // by one for updating upstream interface index by #tetherOffloadRuleUpdate. - coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleA)); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleA)); - inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleB)); - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex); - inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleB)); - - // [3] Clear all rules for a given IpServer. - when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex)) - .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80)); - coordinator.tetherOffloadRuleClear(mIpServer); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleA)); - inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleB)); - inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex); - - // [4] Force pushing stats update to verify that the last diff of stats is reported on all - // upstreams. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated( - new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethIface, 10, 20, 30, 40)) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 50, 60, 70, 80)), - new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40)) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80))); - } - - @Test - public void testTetheringConfigDisable() throws Exception { - setupFunctioningNetdInterface(); - when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - coordinator.startPolling(); - - // The tether stats polling task should not be scheduled. - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - verify(mNetd, never()).tetherOffloadGetStats(); - - // The interface name lookup table can't be added. - final String iface = "rmnet_data0"; - final Integer ifIndex = 100; - coordinator.addUpstreamNameToLookupTable(ifIndex, iface); - assertEquals(0, coordinator.getInterfaceNamesForTesting().size()); - - // The rule can't be added. - final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); - final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a"); - final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = - coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNull(rules); - - // The rule can't be removed. This is not a realistic case because adding rule is not - // allowed. That implies no rule could be removed, cleared or updated. Verify these - // cases just in case. - rules = new LinkedHashMap<Inet6Address, Ipv6ForwardingRule>(); - rules.put(rule.address, rule); - coordinator.getForwardingRulesForTesting().put(mIpServer, rules); - coordinator.tetherOffloadRuleRemove(mIpServer, rule); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNotNull(rules); - assertEquals(1, rules.size()); - - // The rule can't be cleared. - coordinator.tetherOffloadRuleClear(mIpServer); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNotNull(rules); - assertEquals(1, rules.size()); - - // The rule can't be updated. - coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */); - verify(mNetd, never()).tetherOffloadRuleRemove(any()); - verify(mNetd, never()).tetherOffloadRuleAdd(any()); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); - assertNotNull(rules); - assertEquals(1, rules.size()); - } - - @Test - public void testTetheringConfigSetPollingInterval() throws Exception { - setupFunctioningNetdInterface(); - - final BpfCoordinator coordinator = makeBpfCoordinator(); - - // [1] The default polling interval. - coordinator.startPolling(); - assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); - coordinator.stopPolling(); - - // [2] Expect the invalid polling interval isn't applied. The valid range of interval is - // DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. - for (final int interval - : new int[] {0, 100, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS - 1}) { - when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); - coordinator.startPolling(); - assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); - coordinator.stopPolling(); - } - - // [3] Set a specific polling interval which is larger than default value. - // Use a large polling interval to avoid flaky test because the time forwarding - // approximation is used to verify the scheduled time of the polling thread. - final int pollingInterval = 100_000; - when(mTetherConfig.getOffloadPollInterval()).thenReturn(pollingInterval); - coordinator.startPolling(); - - // Expect the specific polling interval to be applied. - assertEquals(pollingInterval, coordinator.getPollingInterval()); - - // Start on a new polling time slot. - mTestLooper.moveTimeForward(pollingInterval); - waitForIdle(); - clearInvocations(mNetd); - - // Move time forward to 90% polling interval time. Expect that the polling thread has not - // scheduled yet. - mTestLooper.moveTimeForward((long) (pollingInterval * 0.9)); - waitForIdle(); - verify(mNetd, never()).tetherOffloadGetStats(); - - // Move time forward to the remaining 10% polling interval time. Expect that the polling - // thread has scheduled. - mTestLooper.moveTimeForward((long) (pollingInterval * 0.1)); - waitForIdle(); - verify(mNetd).tetherOffloadGetStats(); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt deleted file mode 100644 index d915354b0c37..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering - -import android.net.LinkAddress -import android.net.MacAddress -import android.net.TetheredClient -import android.net.TetheredClient.AddressInfo -import android.net.TetheringManager.TETHERING_USB -import android.net.TetheringManager.TETHERING_WIFI -import android.net.ip.IpServer -import android.net.wifi.WifiClient -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -@RunWith(AndroidJUnit4::class) -@SmallTest -class ConnectedClientsTrackerTest { - - private val server1 = mock(IpServer::class.java) - private val server2 = mock(IpServer::class.java) - private val servers = listOf(server1, server2) - - private val clock = TestClock(1324L) - - private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A") - private val client1 = TetheredClient(client1Addr, listOf( - makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)), - TETHERING_WIFI) - private val wifiClient1 = makeWifiClient(client1Addr) - private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB") - private val client2Exp30AddrInfo = makeAddrInfo( - "192.168.43.45/32", "my_hostname", clock.time + 30) - private val client2 = TetheredClient(client2Addr, listOf( - client2Exp30AddrInfo, - makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)), - TETHERING_WIFI) - private val wifiClient2 = makeWifiClient(client2Addr) - private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC") - private val client3 = TetheredClient(client3Addr, - listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)), - TETHERING_USB) - - private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) = - LinkAddress(addr).let { - AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope, - expTime /* deprecationTime */, expTime /* expirationTime */), hostname) - } - - @Test - fun testUpdateConnectedClients() { - doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases - doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases - - val tracker = ConnectedClientsTracker(clock) - assertFalse(tracker.updateConnectedClients(servers, null)) - - // Obtain a lease for client 1 - doReturn(listOf(client1)).`when`(server1).allLeases - assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1))) - - // Client 2 L2-connected, no lease yet - val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI) - assertSameClients(listOf(client1, client2WithoutAddr), - assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2))) - - // Client 2 lease obtained - doReturn(listOf(client1, client2)).`when`(server1).allLeases - assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null)) - - // Client 3 lease obtained - doReturn(listOf(client3)).`when`(server2).allLeases - assertSameClients(listOf(client1, client2, client3), - assertNewClients(tracker, servers, null)) - - // Client 2 L2-disconnected - assertSameClients(listOf(client1, client3), - assertNewClients(tracker, servers, listOf(wifiClient1))) - - // Client 1 L2-disconnected - assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList())) - - // Client 1 comes back - assertSameClients(listOf(client1, client3), - assertNewClients(tracker, servers, listOf(wifiClient1))) - - // Leases lost, client 1 still L2-connected - doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases - doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases - assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)), - assertNewClients(tracker, servers, null)) - } - - @Test - fun testUpdateConnectedClients_LeaseExpiration() { - val tracker = ConnectedClientsTracker(clock) - doReturn(listOf(client1, client2)).`when`(server1).allLeases - doReturn(listOf(client3)).`when`(server2).allLeases - assertSameClients(listOf(client1, client2, client3), assertNewClients( - tracker, servers, listOf(wifiClient1, wifiClient2))) - - clock.time += 20 - // Client 3 has no remaining lease: removed - val expectedClients = listOf( - // Client 1 has no remaining lease but is L2-connected - TetheredClient(client1Addr, emptyList(), TETHERING_WIFI), - // Client 2 has some expired leases - TetheredClient( - client2Addr, - // Only the "t + 30" address is left, the "t + 10" address expired - listOf(client2Exp30AddrInfo), - TETHERING_WIFI)) - assertSameClients(expectedClients, assertNewClients(tracker, servers, null)) - } - - private fun assertNewClients( - tracker: ConnectedClientsTracker, - ipServers: Iterable<IpServer>, - wifiClients: List<WifiClient>? - ): List<TetheredClient> { - assertTrue(tracker.updateConnectedClients(ipServers, wifiClients)) - return tracker.lastTetheredClients - } - - private fun assertSameClients(expected: List<TetheredClient>, actual: List<TetheredClient>) { - val expectedSet = HashSet(expected) - assertEquals(expected.size, expectedSet.size) - assertEquals(expectedSet, HashSet(actual)) - } - - private fun makeWifiClient(macAddr: MacAddress): WifiClient { - // Use a mock WifiClient as the constructor is not part of the WiFi module exported API. - return mock(WifiClient::class.java).apply { doReturn(macAddr).`when`(this).macAddress } - } - - private class TestClock(var time: Long) : ConnectedClientsTracker.Clock() { - override fun elapsedRealtime(): Long { - return time - } - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java deleted file mode 100644 index 354e75356e9f..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.net.util.SharedLog; -import android.os.Bundle; -import android.os.Handler; -import android.os.PersistableBundle; -import android.os.ResultReceiver; -import android.os.SystemProperties; -import android.os.test.TestLooper; -import android.provider.DeviceConfig; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.BroadcastInterceptingContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class EntitlementManagerTest { - - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; - - @Mock private CarrierConfigManager mCarrierConfigManager; - @Mock private Context mContext; - @Mock private Resources mResources; - @Mock private SharedLog mLog; - @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener; - - // Like so many Android system APIs, these cannot be mocked because it is marked final. - // We have to use the real versions. - private final PersistableBundle mCarrierConfig = new PersistableBundle(); - private final TestLooper mLooper = new TestLooper(); - private Context mMockContext; - private Runnable mPermissionChangeCallback; - - private WrappedEntitlementManager mEnMgr; - private TetheringConfiguration mConfig; - private MockitoSession mMockingSession; - - private class MockContext extends BroadcastInterceptingContext { - MockContext(Context base) { - super(base); - } - - @Override - public Resources getResources() { - return mResources; - } - } - - public class WrappedEntitlementManager extends EntitlementManager { - public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; - public int uiProvisionCount = 0; - public int silentProvisionCount = 0; - - public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - super(ctx, h, log, callback); - } - - public void reset() { - fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; - uiProvisionCount = 0; - silentProvisionCount = 0; - } - - @Override - protected Intent runUiTetherProvisioning(int type, - final TetheringConfiguration config, final ResultReceiver receiver) { - Intent intent = super.runUiTetherProvisioning(type, config, receiver); - assertUiTetherProvisioningIntent(type, config, receiver, intent); - uiProvisionCount++; - receiver.send(fakeEntitlementResult, null); - return intent; - } - - private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, - final ResultReceiver receiver, final Intent intent) { - assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - final String[] appName = intent.getStringArrayExtra( - EXTRA_TETHER_UI_PROVISIONING_APP_NAME); - assertEquals(PROVISIONING_APP_NAME.length, appName.length); - for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { - assertEquals(PROVISIONING_APP_NAME[i], appName[i]); - } - assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); - } - - @Override - protected Intent runSilentTetherProvisioning(int type, - final TetheringConfiguration config) { - Intent intent = super.runSilentTetherProvisioning(type, config); - assertSilentTetherProvisioning(type, config, intent); - silentProvisionCount++; - addDownstreamMapping(type, fakeEntitlementResult); - return intent; - } - - private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, - final Intent intent) { - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); - assertEquals(PROVISIONING_NO_UI_APP_NAME, - intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); - assertEquals(PROVISIONING_APP_RESPONSE, - intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); - assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); - } - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mMockingSession = mockitoSession() - .initMocks(this) - .mockStatic(SystemProperties.class) - .mockStatic(DeviceConfig.class) - .strictness(Strictness.WARN) - .startMocking(); - // Don't disable tethering provisioning unless requested. - doReturn(false).when( - () -> SystemProperties.getBoolean( - eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean())); - doReturn(null).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString())); - - when(mResources.getStringArray(R.array.config_tether_dhcp_range)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getIntArray(R.array.config_tether_upstream_types)) - .thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); - when(mLog.forSubComponent(anyString())).thenReturn(mLog); - - mMockContext = new MockContext(mContext); - mPermissionChangeCallback = spy(() -> { }); - mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog, - mPermissionChangeCallback); - mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - mEnMgr.setTetheringConfigurationFetcher(() -> { - return mConfig; - }); - } - - @After - public void tearDown() throws Exception { - mMockingSession.finishMocking(); - } - - private void setupForRequiredProvisioning() { - // Produce some acceptable looking provision app setting if requested. - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(PROVISIONING_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); - // Act like the CarrierConfigManager is present and ready unless told otherwise. - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(mCarrierConfigManager); - when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - } - - @Test - public void canRequireProvisioning() { - setupForRequiredProvisioning(); - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void toleratesCarrierConfigManagerMissing() { - setupForRequiredProvisioning(); - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(null); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. - // Therefore provisioning still be required. - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void toleratesCarrierConfigMissing() { - setupForRequiredProvisioning(); - when(mCarrierConfigManager.getConfig()).thenReturn(null); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - // We still have a provisioning app configured, so still require provisioning. - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void toleratesCarrierConfigNotLoaded() { - setupForRequiredProvisioning(); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false); - // We still have a provisioning app configured, so still require provisioning. - assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void provisioningNotRequiredWhenAppNotFound() { - setupForRequiredProvisioning(); - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(null); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig)); - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(new String[] {"malformedApp"}); - mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig)); - } - - @Test - public void testRequestLastEntitlementCacheValue() throws Exception { - // 1. Entitlement check is not required. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - ResultReceiver receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - - setupForRequiredProvisioning(); - // 2. No cache value and don't need to run entitlement check. - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 3. No cache value and ui entitlement check is needed. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement - // check. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 6. Cache value is TETHER_ERROR_NO_ERROR. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 7. Test get value for other downstream type. - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - // 8. Test get value for invalid downstream type. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - receiver = new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - } - }; - mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - mEnMgr.reset(); - } - - private void assertPermissionChangeCallback(InOrder inOrder) { - inOrder.verify(mPermissionChangeCallback, times(1)).run(); - } - - private void assertNoPermissionChange(InOrder inOrder) { - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void verifyPermissionResult() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); - mLooper.dispatchAll(); - // Permitted: false -> false - assertNoPermissionChange(inOrder); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: false -> true - assertPermissionChangeCallback(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - } - - @Test - public void verifyPermissionIfAllNotApproved() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - // Permitted: false -> false - assertNoPermissionChange(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); - mLooper.dispatchAll(); - // Permitted: false -> false - assertNoPermissionChange(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } - - @Test - public void verifyPermissionIfAnyApproved() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - mEnMgr.notifyUpstream(true); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); - mLooper.dispatchAll(); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } - - @Test - public void verifyPermissionWhenProvisioningNotStarted() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - assertNoPermissionChange(inOrder); - setupForRequiredProvisioning(); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - assertNoPermissionChange(inOrder); - } - - @Test - public void testRunTetherProvisioning() { - final InOrder inOrder = inOrder(mPermissionChangeCallback); - setupForRequiredProvisioning(); - // 1. start ui provisioning, upstream is mobile - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 2. start no-ui provisioning - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(1, mEnMgr.silentProvisionCount); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 3. tear down mobile, then start ui provisioning - mEnMgr.notifyUpstream(false); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - assertNoPermissionChange(inOrder); - mEnMgr.reset(); - - // 4. switch upstream back to mobile - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - // Permitted: true -> true - assertNoPermissionChange(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 5. tear down mobile, then switch SIM - mEnMgr.notifyUpstream(false); - mLooper.dispatchAll(); - mEnMgr.reevaluateSimCardProvisioning(mConfig); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - assertNoPermissionChange(inOrder); - mEnMgr.reset(); - - // 6. switch upstream back to mobile again - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(3, mEnMgr.silentProvisionCount); - // Permitted: true -> false - assertPermissionChangeCallback(inOrder); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 7. start ui provisioning, upstream is mobile, downstream is ethernet - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - // Permitted: false -> true - assertPermissionChangeCallback(inOrder); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - mEnMgr.reset(); - - // 8. downstream is invalid - mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true); - mLooper.dispatchAll(); - assertEquals(0, mEnMgr.uiProvisionCount); - assertEquals(0, mEnMgr.silentProvisionCount); - assertNoPermissionChange(inOrder); - mEnMgr.reset(); - } - - @Test - public void testCallStopTetheringWhenUiProvisioningFail() { - setupForRequiredProvisioning(); - verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI); - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); - mLooper.dispatchAll(); - assertEquals(1, mEnMgr.uiProvisionCount); - verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); - } - - @Test - public void testsetExemptedDownstreamType() throws Exception { - setupForRequiredProvisioning(); - // Cellular upstream is not permitted when no entitlement result. - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - // If there is exempted downstream and no other non-exempted downstreams, cellular is - // permitted. - mEnMgr.setExemptedDownstreamType(TETHERING_WIFI); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - // If second downstream run entitlement check fail, cellular upstream is not permitted. - mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; - mEnMgr.notifyUpstream(true); - mLooper.dispatchAll(); - mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); - mLooper.dispatchAll(); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - - // When second downstream is down, exempted downstream can use cellular upstream. - assertEquals(1, mEnMgr.uiProvisionCount); - verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB); - mEnMgr.stopProvisioningIfNeeded(TETHERING_USB); - assertTrue(mEnMgr.isCellularUpstreamPermitted()); - - mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); - assertFalse(mEnMgr.isCellularUpstreamPermitted()); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java deleted file mode 100644 index f2b5314e5a17..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.ip.IpServer.STATE_LOCAL_ONLY; -import static android.net.ip.IpServer.STATE_TETHERED; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.InetAddresses; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.ip.IpServer; -import android.net.util.SharedLog; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IPv6TetheringCoordinatorTest { - private static final String TEST_DNS_SERVER = "2001:4860:4860::8888"; - private static final String TEST_INTERFACE = "test_rmnet0"; - private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64"; - private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24"; - - private IPv6TetheringCoordinator mIPv6TetheringCoordinator; - private ArrayList<IpServer> mNotifyList; - - @Mock private SharedLog mSharedLog; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); - mNotifyList = new ArrayList<IpServer>(); - mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog); - } - - private UpstreamNetworkState createDualStackUpstream(final int transportType) { - final Network network = mock(Network.class); - final NetworkCapabilities netCap = - new NetworkCapabilities.Builder().addTransportType(transportType).build(); - final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER); - final LinkProperties linkProp = new LinkProperties(); - linkProp.setInterfaceName(TEST_INTERFACE); - linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS)); - linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS)); - linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST)); - linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE, - RTN_UNICAST)); - linkProp.addDnsServer(dns); - return new UpstreamNetworkState(linkProp, netCap, network); - } - - private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) { - assertEquals(lp.getInterfaceName(), TEST_INTERFACE); - assertFalse(lp.hasIpv4Address()); - final List<LinkAddress> addresses = lp.getLinkAddresses(); - assertEquals(addresses.size(), 1); - final LinkAddress v6Address = addresses.get(0); - assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS)); - } - - @Test - public void testUpdateIpv6Upstream() throws Exception { - // 1. Add first IpServer. - final IpServer firstServer = mock(IpServer.class); - mNotifyList.add(firstServer); - mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED); - verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - verifyNoMoreInteractions(firstServer); - - // 2. Add second IpServer and it would not have ipv6 tethering. - final IpServer secondServer = mock(IpServer.class); - mNotifyList.add(secondServer); - mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY); - verifyNoMoreInteractions(secondServer); - reset(firstServer, secondServer); - - // 3. No upstream. - mIPv6TetheringCoordinator.updateUpstreamNetworkState(null); - verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - reset(firstServer, secondServer); - - // 4. Update ipv6 mobile upstream. - final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); - final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class); - mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); - verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), - lp.capture()); - final LinkProperties v6OnlyLink = lp.getValue(); - assertOnlyOneV6AddressAndNoV4(v6OnlyLink); - verifyNoMoreInteractions(firstServer); - verifyNoMoreInteractions(secondServer); - reset(firstServer, secondServer); - - // 5. Remove first IpServer. - mNotifyList.remove(firstServer); - mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); - verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), - lp.capture()); - final LinkProperties localOnlyLink = lp.getValue(); - assertNotNull(localOnlyLink); - assertNotEquals(localOnlyLink, v6OnlyLink); - reset(firstServer, secondServer); - - // 6. Remove second IpServer. - mNotifyList.remove(secondServer); - mIPv6TetheringCoordinator.removeActiveDownstream(secondServer); - verifyNoMoreInteractions(firstServer); - verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java deleted file mode 100644 index 071a290e657b..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import static android.Manifest.permission.WRITE_SETTINGS; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import static org.mockito.Mockito.mock; - -import android.content.Context; -import android.content.Intent; -import android.net.ITetheringConnector; -import android.os.Binder; -import android.os.IBinder; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class MockTetheringService extends TetheringService { - private final Tethering mTethering = mock(Tethering.class); - - @Override - public IBinder onBind(Intent intent) { - return new MockTetheringConnector(super.onBind(intent)); - } - - @Override - public Tethering makeTethering(TetheringDependencies deps) { - return mTethering; - } - - @Override - boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, - @NonNull String callingPackage, @Nullable String callingAttributionTag, - boolean throwException) { - // Test this does not verify the calling package / UID, as calling package could be shell - // and not match the UID. - return context.checkCallingOrSelfPermission(WRITE_SETTINGS) == PERMISSION_GRANTED; - } - - public Tethering getTethering() { - return mTethering; - } - - public class MockTetheringConnector extends Binder { - final IBinder mBase; - MockTetheringConnector(IBinder base) { - mBase = base; - } - - public ITetheringConnector getTetheringConnector() { - return ITetheringConnector.Stub.asInterface(mBase); - } - - public MockTetheringService getService() { - return MockTetheringService.this; - } - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java deleted file mode 100644 index ce52ae22ece8..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStats.UID_TETHERING; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; - -import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; -import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; -import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; -import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; -import static com.android.testutils.MiscAsserts.assertContainsAll; -import static com.android.testutils.MiscAsserts.assertThrows; -import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; - -import static junit.framework.Assert.assertNotNull; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.usage.NetworkStatsManager; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.net.ITetheringStatsProvider; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; -import android.net.RouteInfo; -import android.net.netstats.provider.NetworkStatsProvider; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.test.TestLooper; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.test.mock.MockContentResolver; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.FakeSettingsProvider; -import com.android.testutils.TestableNetworkStatsProviderCbBinder; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class OffloadControllerTest { - private static final String RNDIS0 = "test_rndis0"; - private static final String RMNET0 = "test_rmnet_data0"; - private static final String WLAN0 = "test_wlan0"; - - private static final String IPV6_LINKLOCAL = "fe80::/64"; - private static final String IPV6_DOC_PREFIX = "2001:db8::/64"; - private static final String IPV6_DISCARD_PREFIX = "100::/64"; - private static final String USB_PREFIX = "192.168.42.0/24"; - private static final String WIFI_PREFIX = "192.168.43.0/24"; - private static final long WAIT_FOR_IDLE_TIMEOUT = 2 * 1000; - - @Mock private OffloadHardwareInterface mHardware; - @Mock private ApplicationInfo mApplicationInfo; - @Mock private Context mContext; - @Mock private NetworkStatsManager mStatsManager; - @Mock private TetheringConfiguration mTetherConfig; - // Late init since methods must be called by the thread that created this object. - private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; - private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; - private final ArgumentCaptor<ArrayList> mStringArrayCaptor = - ArgumentCaptor.forClass(ArrayList.class); - private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor = - ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); - private MockContentResolver mContentResolver; - private final TestLooper mTestLooper = new TestLooper(); - private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { - @Override - public TetheringConfiguration getTetherConfig() { - return mTetherConfig; - } - }; - - @Before public void setUp() { - MockitoAnnotations.initMocks(this); - when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); - when(mContext.getPackageName()).thenReturn("OffloadControllerTest"); - mContentResolver = new MockContentResolver(mContext); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - when(mContext.getContentResolver()).thenReturn(mContentResolver); - FakeSettingsProvider.clearSettingsProvider(); - when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled. - } - - @After public void tearDown() throws Exception { - FakeSettingsProvider.clearSettingsProvider(); - } - - private void setupFunctioningHardwareInterface() { - when(mHardware.initOffloadConfig()).thenReturn(true); - when(mHardware.initOffloadControl(mControlCallbackCaptor.capture())) - .thenReturn(true); - when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true); - when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats()); - when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); - } - - private void enableOffload() { - Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); - } - - private void setOffloadPollInterval(int interval) { - when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); - } - - private void waitForIdle() { - mTestLooper.dispatchAll(); - } - - private OffloadController makeOffloadController() throws Exception { - OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()), - mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); - final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider> - tetherStatsProviderCaptor = - ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); - verify(mStatsManager).registerNetworkStatsProvider(anyString(), - tetherStatsProviderCaptor.capture()); - mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); - assertNotNull(mTetherStatsProvider); - mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); - mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); - return offload; - } - - @Test - public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception { - setupFunctioningHardwareInterface(); - when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1); - assertThrows(SettingNotFoundException.class, () -> - Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, never()).initOffloadConfig(); - inOrder.verify(mHardware, never()).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception { - setupFunctioningHardwareInterface(); - when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0); - assertThrows(SettingNotFoundException.class, () -> - Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testSettingsAllowsStart() throws Exception { - setupFunctioningHardwareInterface(); - Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testSettingsDisablesStart() throws Exception { - setupFunctioningHardwareInterface(); - Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, never()).initOffloadConfig(); - inOrder.verify(mHardware, never()).initOffloadControl(anyObject()); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testSetUpstreamLinkPropertiesWorking() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - - // In reality, the UpstreamNetworkMonitor would have passed down to us - // a covering set of local prefixes representing a minimum essential - // set plus all the prefixes on networks with network agents. - // - // We simulate that there, and then add upstream elements one by one - // and watch what happens. - final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); - for (String s : new String[]{ - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { - minimumLocalPrefixes.add(new IpPrefix(s)); - } - offload.setLocalPrefixes(minimumLocalPrefixes); - inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); - ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); - assertEquals(4, localPrefixes.size()); - assertContainsAll(localPrefixes, - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); - inOrder.verifyNoMoreInteractions(); - - offload.setUpstreamLinkProperties(null); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - // This LinkProperties value does not differ from the default upstream. - // There should be no extraneous call to setUpstreamParameters(). - inOrder.verify(mHardware, never()).setUpstreamParameters( - anyObject(), anyObject(), anyObject(), anyObject()); - inOrder.verifyNoMoreInteractions(); - - final LinkProperties lp = new LinkProperties(); - - final String testIfName = "rmnet_data17"; - lp.setInterfaceName(testIfName); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(null), eq(null), eq(null)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv4Addr = "192.0.2.5"; - final String linkAddr = ipv4Addr + "/24"; - lp.addLinkAddress(new LinkAddress(linkAddr)); - lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // IPv4 prefixes and addresses on the upstream are simply left as whole - // prefixes (already passed in from UpstreamNetworkMonitor code). If a - // tethering client sends traffic to the IPv4 default router or other - // clients on the upstream this will not be hardware-forwarded, and that - // should be fine for now. Ergo: no change in local addresses, no call - // to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(null), eq(null)); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv4Gateway = "192.0.2.1"; - lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null)); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv6Gw1 = "fe80::cafe"; - lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - ArrayList<String> v6gws = mStringArrayCaptor.getValue(); - assertEquals(1, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final String ipv6Gw2 = "fe80::d00d"; - lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - v6gws = mStringArrayCaptor.getValue(); - assertEquals(2, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - assertTrue(v6gws.contains(ipv6Gw2)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - final LinkProperties stacked = new LinkProperties(); - stacked.setInterfaceName("stacked"); - stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); - stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null, - RTN_UNICAST)); - stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null, - RTN_UNICAST)); - assertTrue(lp.addStackedLink(stacked)); - offload.setUpstreamLinkProperties(lp); - // No change in local addresses means no call to setLocalPrefixes(). - inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - v6gws = mStringArrayCaptor.getValue(); - assertEquals(2, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - assertTrue(v6gws.contains(ipv6Gw2)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - // Add in some IPv6 upstream info. When there is a tethered downstream - // making use of the IPv6 prefix we would expect to see the /64 route - // removed from "local prefixes" and /128s added for the upstream IPv6 - // addresses. This is not yet implemented, and for now we simply - // expect to see these /128s. - lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST)); - // "2001:db8::/64" plus "assigned" ASCII in hex - lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); - // "2001:db8::/64" plus "random" ASCII in hex - lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64")); - offload.setUpstreamLinkProperties(lp); - inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); - localPrefixes = mStringArrayCaptor.getValue(); - assertEquals(6, localPrefixes.size()); - assertContainsAll(localPrefixes, - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64", - "2001:db8::6173:7369:676e:6564/128", "2001:db8::7261:6e64:6f6d/128"); - // The relevant parts of the LinkProperties have not changed, but at the - // moment we do not de-dup upstream LinkProperties this carefully. - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); - v6gws = mStringArrayCaptor.getValue(); - assertEquals(2, v6gws.size()); - assertTrue(v6gws.contains(ipv6Gw1)); - assertTrue(v6gws.contains(ipv6Gw2)); - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); - inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); - inOrder.verifyNoMoreInteractions(); - - // Completely identical LinkProperties updates are de-duped. - offload.setUpstreamLinkProperties(lp); - // This LinkProperties value does not differ from the default upstream. - // There should be no extraneous call to setUpstreamParameters(). - inOrder.verify(mHardware, never()).setUpstreamParameters( - anyObject(), anyObject(), anyObject(), anyObject()); - inOrder.verifyNoMoreInteractions(); - } - - private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how, - @NonNull String iface, long rxBytes, long txBytes) { - return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT, - TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L, - txBytes, 0L, 0L); - } - - @Test - public void testGetForwardedStats() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final String ethernetIface = "eth1"; - final String mobileIface = "rmnet_data0"; - - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(12345, 54321)); - when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn( - new ForwardedStats(999, 99999)); - - InOrder inOrder = inOrder(mHardware); - - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - // Previous upstream was null, so no stats are fetched. - inOrder.verify(mHardware, never()).getForwardedStats(any()); - - lp.setInterfaceName(mobileIface); - offload.setUpstreamLinkProperties(lp); - // Expect that we fetch stats from the previous upstream. - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); - - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - // Expect that we fetch stats from the previous upstream. - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface)); - - // Verify that the fetched stats are stored. - final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); - final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID); - final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); - - final NetworkStats expectedUidStats = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); - - assertNetworkStatsEquals(expectedIfaceStats, ifaceStats); - assertNetworkStatsEquals(expectedUidStats, uidStats); - - // Force pushing stats update to verify the stats reported. - mTetherStatsProvider.pushTetherStats(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); - - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(100000, 100000)); - offload.setUpstreamLinkProperties(null); - // Expect that we first clear the HAL's upstream parameters. - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null)); - // Expect that we fetch stats from the previous upstream. - inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); - - // There is no current upstream, so no stats are fetched. - inOrder.verify(mHardware, never()).getForwardedStats(any()); - inOrder.verifyNoMoreInteractions(); - - // Verify that the stored stats is accumulated. - final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); - final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID); - final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); - - final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) - .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); - - assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu); - assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu); - - // Verify that only diff of stats is reported. - mTetherStatsProvider.pushTetherStats(); - final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); - - final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) - .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) - .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); - mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, - expectedUidStatsDiff); - } - - @Test - public void testSetInterfaceQuota() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final String ethernetIface = "eth1"; - final String mobileIface = "rmnet_data0"; - final long ethernetLimit = 12345; - final long mobileLimit = 12345678; - - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - - final InOrder inOrder = inOrder(mHardware); - when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); - when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); - - // Applying an interface quota to the current upstream immediately sends it to the hardware. - mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit); - waitForIdle(); - inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); - inOrder.verifyNoMoreInteractions(); - - // Applying an interface quota to another upstream does not take any immediate action. - mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); - waitForIdle(); - inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); - - // Switching to that upstream causes the quota to be applied if the parameters were applied - // correctly. - lp.setInterfaceName(mobileIface); - offload.setUpstreamLinkProperties(lp); - waitForIdle(); - inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit); - - // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set - // to Long.MAX_VALUE. - mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); - waitForIdle(); - inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); - - // If setting upstream parameters fails, then the data limit is not set. - when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); - waitForIdle(); - inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); - - // If setting the data limit fails while changing upstreams, offload is stopped. - when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); - when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); - lp.setInterfaceName(mobileIface); - offload.setUpstreamLinkProperties(lp); - mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); - waitForIdle(); - inOrder.verify(mHardware).getForwardedStats(ethernetIface); - inOrder.verify(mHardware).stopOffloadControl(); - } - - @Test - public void testDataLimitCallback() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); - callback.onStoppedLimitReached(); - mTetherStatsProviderCb.expectNotifyStatsUpdated(); - } - - @Test - public void testAddRemoveDownstreams() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - final InOrder inOrder = inOrder(mHardware); - inOrder.verify(mHardware, times(1)).initOffloadConfig(); - inOrder.verify(mHardware, times(1)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); - inOrder.verifyNoMoreInteractions(); - - // Tethering makes several calls to setLocalPrefixes() before add/remove - // downstream calls are made. This is not tested here; only the behavior - // of notifyDownstreamLinkProperties() and removeDownstreamInterface() - // are tested. - - // [1] USB tethering is started. - final LinkProperties usbLinkProperties = new LinkProperties(); - usbLinkProperties.setInterfaceName(RNDIS0); - usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX); - inOrder.verifyNoMoreInteractions(); - - // [2] Routes for IPv6 link-local prefixes should never be added. - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); - inOrder.verifyNoMoreInteractions(); - - // [3] Add an IPv6 prefix for good measure. Only new offload-able - // prefixes should be passed to the HAL. - usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); - inOrder.verifyNoMoreInteractions(); - - // [4] Adding addresses doesn't affect notifyDownstreamLinkProperties(). - // The address is passed in by a separate setLocalPrefixes() invocation. - usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::2/64")); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); - - // [5] Differences in local routes are converted into addDownstream() - // and removeDownstream() invocations accordingly. - usbLinkProperties.removeRoute( - new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST)); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); - inOrder.verifyNoMoreInteractions(); - - // [6] Removing a downstream interface which was never added causes no - // interactions with the HAL. - offload.removeDownstreamInterface(WLAN0); - inOrder.verifyNoMoreInteractions(); - - // [7] Removing an active downstream removes all remaining prefixes. - offload.removeDownstreamInterface(RNDIS0); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, USB_PREFIX); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - // Pretend to set a few different upstreams (only the interface name - // matters for this test; we're ignoring IP and route information). - final LinkProperties upstreamLp = new LinkProperties(); - for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { - upstreamLp.setInterfaceName(ifname); - offload.setUpstreamLinkProperties(upstreamLp); - } - - // Clear invocation history, especially the getForwardedStats() calls - // that happen with setUpstreamParameters(). - clearInvocations(mHardware); - - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); - callback.onStoppedUnsupported(); - - // Verify forwarded stats behaviour. - verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); - verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - // TODO: verify the exact stats reported. - mTetherStatsProviderCb.expectNotifyStatsUpdated(); - mTetherStatsProviderCb.assertNoCallback(); - verifyNoMoreInteractions(mHardware); - } - - @Test - public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters() - throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - - final OffloadController offload = makeOffloadController(); - offload.start(); - - // Pretend to set a few different upstreams (only the interface name - // matters for this test; we're ignoring IP and route information). - final LinkProperties upstreamLp = new LinkProperties(); - for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { - upstreamLp.setInterfaceName(ifname); - offload.setUpstreamLinkProperties(upstreamLp); - } - - // Pretend that some local prefixes and downstreams have been added - // (and removed, for good measure). - final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); - for (String s : new String[]{ - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { - minimumLocalPrefixes.add(new IpPrefix(s)); - } - offload.setLocalPrefixes(minimumLocalPrefixes); - - final LinkProperties usbLinkProperties = new LinkProperties(); - usbLinkProperties.setInterfaceName(RNDIS0); - usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); - usbLinkProperties.addRoute( - new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(usbLinkProperties); - - final LinkProperties wifiLinkProperties = new LinkProperties(); - wifiLinkProperties.setInterfaceName(WLAN0); - wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24")); - wifiLinkProperties.addRoute( - new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST)); - wifiLinkProperties.addRoute( - new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); - // Use a benchmark prefix (RFC 5180 + erratum), since the documentation - // prefix is included in the excluded prefix list. - wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64")); - wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64")); - wifiLinkProperties.addRoute( - new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST)); - offload.notifyDownstreamLinkProperties(wifiLinkProperties); - - offload.removeDownstreamInterface(RNDIS0); - - // Clear invocation history, especially the getForwardedStats() calls - // that happen with setUpstreamParameters(). - clearInvocations(mHardware); - - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); - callback.onSupportAvailable(); - - // Verify forwarded stats behaviour. - verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); - verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - mTetherStatsProviderCb.expectNotifyStatsUpdated(); - mTetherStatsProviderCb.assertNoCallback(); - - // TODO: verify local prefixes and downstreams are also pushed to the HAL. - verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); - ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); - assertEquals(4, localPrefixes.size()); - assertContainsAll(localPrefixes, - // TODO: The logic to find and exclude downstream IP prefixes - // is currently in Tethering's OffloadWrapper but must be moved - // into OffloadController proper. After this, also check for: - // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128" - "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); - verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24"); - verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64"); - verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any()); - verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong()); - verifyNoMoreInteractions(mHardware); - } - - @Test - public void testOnSetAlert() throws Exception { - setupFunctioningHardwareInterface(); - enableOffload(); - setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - final OffloadController offload = makeOffloadController(); - offload.start(); - - // Initialize with fake eth upstream. - final String ethernetIface = "eth1"; - InOrder inOrder = inOrder(mHardware); - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(ethernetIface); - offload.setUpstreamLinkProperties(lp); - // Previous upstream was null, so no stats are fetched. - inOrder.verify(mHardware, never()).getForwardedStats(any()); - - // Verify that set quota to 0 will immediately triggers an callback. - mTetherStatsProvider.onSetAlert(0); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that notifyAlertReached never fired if quota is not yet reached. - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(0, 0)); - mTetherStatsProvider.onSetAlert(100); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - - // Verify that notifyAlertReached fired when quota is reached. - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( - new ForwardedStats(50, 50)); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.expectNotifyAlertReached(); - - // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch - // any stats since the polling is stopped. - reset(mHardware); - mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); - mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - waitForIdle(); - mTetherStatsProviderCb.assertNoCallback(); - verify(mHardware, never()).getForwardedStats(any()); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java deleted file mode 100644 index 38b19dd3da5c..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.util.TetheringUtils.uint16; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_UNIX; -import static android.system.OsConstants.SOCK_STREAM; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; -import android.hardware.tetheroffload.control.V1_0.IOffloadControl; -import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; -import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; -import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; -import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; -import android.net.netlink.StructNfGenMsg; -import android.net.netlink.StructNlMsgHdr; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.NativeHandle; -import android.os.test.TestLooper; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.FileDescriptor; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class OffloadHardwareInterfaceTest { - private static final String RMNET0 = "test_rmnet_data0"; - - private final TestLooper mTestLooper = new TestLooper(); - - private OffloadHardwareInterface mOffloadHw; - private ITetheringOffloadCallback mTetheringOffloadCallback; - private OffloadHardwareInterface.ControlCallback mControlCallback; - - @Mock private IOffloadConfig mIOffloadConfig; - @Mock private IOffloadControl mIOffloadControl; - @Mock private NativeHandle mNativeHandle; - - // Random values to test Netlink message. - private static final short TEST_TYPE = 184; - private static final short TEST_FLAGS = 263; - - class MyDependencies extends OffloadHardwareInterface.Dependencies { - MyDependencies(SharedLog log) { - super(log); - } - - @Override - public IOffloadConfig getOffloadConfig() { - return mIOffloadConfig; - } - - @Override - public IOffloadControl getOffloadControl() { - return mIOffloadControl; - } - - @Override - public NativeHandle createConntrackSocket(final int groups) { - return mNativeHandle; - } - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - final SharedLog log = new SharedLog("test"); - mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log, - new MyDependencies(log)); - mControlCallback = spy(new OffloadHardwareInterface.ControlCallback()); - } - - private void startOffloadHardwareInterface() throws Exception { - mOffloadHw.initOffloadConfig(); - mOffloadHw.initOffloadControl(mControlCallback); - final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor = - ArgumentCaptor.forClass(ITetheringOffloadCallback.class); - verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any()); - mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue(); - } - - @Test - public void testGetForwardedStats() throws Exception { - startOffloadHardwareInterface(); - final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0); - verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any()); - assertNotNull(stats); - } - - @Test - public void testSetLocalPrefixes() throws Exception { - startOffloadHardwareInterface(); - final ArrayList<String> localPrefixes = new ArrayList<>(); - localPrefixes.add("127.0.0.0/8"); - localPrefixes.add("fe80::/64"); - mOffloadHw.setLocalPrefixes(localPrefixes); - verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any()); - } - - @Test - public void testSetDataLimit() throws Exception { - startOffloadHardwareInterface(); - final long limit = 12345; - mOffloadHw.setDataLimit(RMNET0, limit); - verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any()); - } - - @Test - public void testSetUpstreamParameters() throws Exception { - startOffloadHardwareInterface(); - final String v4addr = "192.168.10.1"; - final String v4gateway = "192.168.10.255"; - final ArrayList<String> v6gws = new ArrayList<>(0); - v6gws.add("2001:db8::1"); - mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws); - verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), - eq(v6gws), any()); - - final ArgumentCaptor<ArrayList<String>> mArrayListCaptor = - ArgumentCaptor.forClass(ArrayList.class); - mOffloadHw.setUpstreamParameters(null, null, null, null); - verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""), - mArrayListCaptor.capture(), any()); - assertEquals(mArrayListCaptor.getValue().size(), 0); - } - - @Test - public void testUpdateDownstreamPrefix() throws Exception { - startOffloadHardwareInterface(); - final String ifName = "wlan1"; - final String prefix = "192.168.43.0/24"; - mOffloadHw.addDownstreamPrefix(ifName, prefix); - verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any()); - - mOffloadHw.removeDownstreamPrefix(ifName, prefix); - verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any()); - } - - @Test - public void testTetheringOffloadCallback() throws Exception { - startOffloadHardwareInterface(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStarted(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedError(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedUnsupported(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE); - mTestLooper.dispatchAll(); - verify(mControlCallback).onSupportAvailable(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedLimitReached(); - - final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP); - mTetheringOffloadCallback.updateTimeout(tcpParams); - mTestLooper.dispatchAll(); - verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP), - eq(tcpParams.src.addr), - eq(uint16(tcpParams.src.port)), - eq(tcpParams.dst.addr), - eq(uint16(tcpParams.dst.port))); - - final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP); - mTetheringOffloadCallback.updateTimeout(udpParams); - mTestLooper.dispatchAll(); - verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP), - eq(udpParams.src.addr), - eq(uint16(udpParams.src.port)), - eq(udpParams.dst.addr), - eq(uint16(udpParams.dst.port))); - } - - @Test - public void testSendIpv4NfGenMsg() throws Exception { - FileDescriptor writeSocket = new FileDescriptor(); - FileDescriptor readSocket = new FileDescriptor(); - try { - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, writeSocket, readSocket); - } catch (ErrnoException e) { - fail(); - return; - } - when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket); - - mOffloadHw.sendIpv4NfGenMsg(mNativeHandle, TEST_TYPE, TEST_FLAGS); - - ByteBuffer buffer = ByteBuffer.allocate(9823); // Arbitrary value > expectedLen. - buffer.order(ByteOrder.nativeOrder()); - - int read = Os.read(readSocket, buffer); - final int expectedLen = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; - assertEquals(expectedLen, read); - - buffer.flip(); - assertEquals(expectedLen, buffer.getInt()); - assertEquals(TEST_TYPE, buffer.getShort()); - assertEquals(TEST_FLAGS, buffer.getShort()); - assertEquals(0 /* seq */, buffer.getInt()); - assertEquals(0 /* pid */, buffer.getInt()); - assertEquals(AF_INET, buffer.get()); // nfgen_family - assertEquals(0 /* error */, buffer.get()); // version - assertEquals(0 /* error */, buffer.getShort()); // res_id - assertEquals(expectedLen, buffer.position()); - } - - private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { - final NatTimeoutUpdate params = new NatTimeoutUpdate(); - params.proto = proto; - params.src.addr = "192.168.43.200"; - params.src.port = 100; - params.dst.addr = "172.50.46.169"; - params.dst.port = 150; - return params; - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java deleted file mode 100644 index 41d46e522ca4..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.networkstack.tethering; - -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHERING_WIFI_P2P; -import static android.net.util.PrefixUtils.asIpPrefix; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.ip.IpServer; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class PrivateAddressCoordinatorTest { - private static final String TEST_IFNAME = "test0"; - - @Mock private IpServer mHotspotIpServer; - @Mock private IpServer mUsbIpServer; - @Mock private IpServer mEthernetIpServer; - @Mock private IpServer mWifiP2pIpServer; - @Mock private Context mContext; - @Mock private ConnectivityManager mConnectivityMgr; - @Mock private TetheringConfiguration mConfig; - - private PrivateAddressCoordinator mPrivateAddressCoordinator; - private final LinkAddress mBluetoothAddress = new LinkAddress("192.168.44.1/24"); - private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24"); - private final Network mWifiNetwork = new Network(1); - private final Network mMobileNetwork = new Network(2); - private final Network mVpnNetwork = new Network(3); - private final Network mMobileNetwork2 = new Network(4); - private final Network mMobileNetwork3 = new Network(5); - private final Network mMobileNetwork4 = new Network(6); - private final Network mMobileNetwork5 = new Network(7); - private final Network mMobileNetwork6 = new Network(8); - private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork, - mMobileNetwork2, mMobileNetwork3, mMobileNetwork4, mMobileNetwork5, mMobileNetwork6}; - private final ArrayList<IpPrefix> mTetheringPrefixes = new ArrayList<>(Arrays.asList( - new IpPrefix("192.168.0.0/16"), - new IpPrefix("172.16.0.0/12"), - new IpPrefix("10.0.0.0/8"))); - - private void setUpIpServers() throws Exception { - when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); - when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET); - when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); - when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); - when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); - when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false); - when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true); - setUpIpServers(); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig)); - } - - private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) { - final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( - ipServer, useLastAddress); - when(ipServer.getAddress()).thenReturn(address); - return address; - } - - @Test - public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception { - final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress); - final LinkAddress address = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(address); - assertNotEquals(hotspotPrefix, bluetoothPrefix); - - final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - final IpPrefix testDupRequest = asIpPrefix(newAddress); - assertNotEquals(hotspotPrefix, testDupRequest); - assertNotEquals(bluetoothPrefix, testDupRequest); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, - false /* useLastAddress */); - final IpPrefix usbPrefix = asIpPrefix(usbAddress); - assertNotEquals(usbPrefix, bluetoothPrefix); - assertNotEquals(usbPrefix, hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); - } - - @Test - public void testSanitizedAddress() throws Exception { - int fakeSubAddr = 0x2b00; // 43.0. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - fakeSubAddr = 0x2d01; // 45.1. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - fakeSubAddr = 0x2eff; // 46.255. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - fakeSubAddr = 0x2f05; // 47.5. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); - assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - } - - @Test - public void testReservedPrefix() throws Exception { - // - Test bluetooth prefix is reserved. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(mBluetoothAddress.getAddress().getAddress())); - final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer, - false /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress); - assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - - // - Test previous enabled hotspot prefix(cached prefix) is reserved. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(hotspotAddress.getAddress().getAddress())); - final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, - false /* useLastAddress */); - final IpPrefix usbPrefix = asIpPrefix(usbAddress); - assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix); - assertNotEquals(hotspotPrefix, usbPrefix); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); - - // - Test wifi p2p prefix is reserved. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); - final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer, - false /* useLastAddress */); - final IpPrefix etherPrefix = asIpPrefix(etherAddress); - assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix); - assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix); - assertNotEquals(hotspotPrefix, etherPrefix); - mPrivateAddressCoordinator.releaseDownstream(mEthernetIpServer); - } - - @Test - public void testRequestLastDownstreamAddress() throws Exception { - final int fakeHotspotSubAddr = 0x2b05; // 43.5 - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress); - - final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress); - - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); - - final int newFakeSubAddr = 0x3c05; - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - - final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals(hotspotAddress, newHotspotAddress); - final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals(usbAddress, newUsbAddress); - - final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.88.23/16"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); - verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - verify(mUsbIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - } - - private UpstreamNetworkState buildUpstreamNetworkState(final Network network, - final LinkAddress v4Addr, final LinkAddress v6Addr, final NetworkCapabilities cap) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_IFNAME); - if (v4Addr != null) prop.addLinkAddress(v4Addr); - - if (v6Addr != null) prop.addLinkAddress(v6Addr); - - return new UpstreamNetworkState(prop, cap, network); - } - - private NetworkCapabilities makeNetworkCapabilities(final int transportType) { - final NetworkCapabilities cap = new NetworkCapabilities(); - cap.addTransportType(transportType); - if (transportType == TRANSPORT_VPN) { - cap.removeCapability(NET_CAPABILITY_NOT_VPN); - } - - return cap; - } - - @Test - public void testNoConflictUpstreamPrefix() throws Exception { - final int fakeHotspotSubAddr = 0x2b05; // 43.5 - final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24"); - // Force always get subAddress "43.5" for conflict testing. - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - // - Enable hotspot with prefix 192.168.43.0/24 - final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr); - assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix); - // - test mobile network with null NetworkCapabilities. Ideally this should not happen - // because NetworkCapabilities update should always happen before LinkProperties update - // and the UpstreamNetworkState update, just make sure no crash in this case. - final UpstreamNetworkState noCapUpstream = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("10.0.0.8/24"), null, null); - mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - test mobile upstream with no address. - final UpstreamNetworkState noAddress = buildUpstreamNetworkState(mMobileNetwork, - null, null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v6 only mobile network, hotspot prefix should not be removed. - final UpstreamNetworkState v6OnlyMobile = buildUpstreamNetworkState(mMobileNetwork, - null, new LinkAddress("2001:db8::/64"), - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyMobile); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork); - // - Update v4 only mobile network, hotspot prefix should not be removed. - final UpstreamNetworkState v4OnlyMobile = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("10.0.0.8/24"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyMobile); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v4v6 mobile network, hotspot prefix should not be removed. - final UpstreamNetworkState v4v6Mobile = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("10.0.0.8/24"), new LinkAddress("2001:db8::/64"), - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4v6Mobile); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v6 only wifi network, hotspot prefix should not be removed. - final UpstreamNetworkState v6OnlyWifi = buildUpstreamNetworkState(mWifiNetwork, - null, new LinkAddress("2001:db8::/64"), makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyWifi); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); - // - Update vpn network, it conflict with hotspot prefix but VPN networks are ignored. - final UpstreamNetworkState v4OnlyVpn = buildUpstreamNetworkState(mVpnNetwork, - new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_VPN)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyVpn); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Update v4 only wifi network, it conflict with hotspot prefix. - final UpstreamNetworkState v4OnlyWifi = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); - verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - reset(mHotspotIpServer); - // - Restart hotspot again and its prefix is different previous. - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2); - assertNotEquals(hotspotPrefix, hotspotPrefix2); - mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); - verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // - Usb tethering can be enabled and its prefix is different with conflict one. - final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - final IpPrefix usbPrefix = asIpPrefix(usbAddr); - assertNotEquals(predefinedPrefix, usbPrefix); - assertNotEquals(hotspotPrefix2, usbPrefix); - // - Disable wifi upstream, then wifi's prefix can be selected again. - mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); - final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer, - true /* useLastAddress */); - final IpPrefix ethPrefix = asIpPrefix(ethAddr); - assertEquals(predefinedPrefix, ethPrefix); - } - - @Test - public void testChooseAvailablePrefix() throws Exception { - final int randomAddress = 0x8605; // 134.5 - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress); - final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5. - assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0); - final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.134.13/26"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); - - // Check whether return address is next prefix of 192.168.134.0/24. - final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1); - final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.149.16/19"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream2); - - - // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24. - final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2); - final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("192.168.129.53/18"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - // Update another conflict upstream which is covered by the previous one (but not the first - // one) and verify whether this would affect the result. - final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2, - new LinkAddress("192.168.170.7/19"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2); - - // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24. - final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3); - final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3, - new LinkAddress("192.168.188.133/17"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3); - - // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because - // 192.168.134/24 ~ 192.168.255.255/24 is not available. - final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4); - final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4, - new LinkAddress("192.168.3.59/21"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4); - - // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24. - final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5); - final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5, - new LinkAddress("192.168.68.43/21"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5); - - // Update an upstream that does *not* conflict, check whether return the same address - // 192.168.5/24. - final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6); - final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6, - new LinkAddress("192.168.10.97/21"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6); - - // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24. - final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7); - final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6, - new LinkAddress("192.168.0.0/17"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7); - - // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16. - final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8); - } - - @Test - public void testChoosePrefixFromDifferentRanges() throws Exception { - final int randomAddress = 0x1f2b2a; // 31.43.42 - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress); - final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42. - assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1); - final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, - new LinkAddress("192.168.88.23/17"), null, - makeNetworkCapabilities(TRANSPORT_WIFI)); - mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); - verifyNotifyConflictAndRelease(mHotspotIpServer); - - // Check whether return address is next address of prefix 192.168.128.0/17. - final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2); - final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork, - new LinkAddress("192.1.2.3/8"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream); - verifyNotifyConflictAndRelease(mHotspotIpServer); - - // Check whether return address is under prefix 172.16.0.0/12. - final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1); - final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2, - new LinkAddress("172.28.123.100/14"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2); - verifyNotifyConflictAndRelease(mHotspotIpServer); - - // 172.28.0.0 ~ 172.31.255.255 is not available. - // Check whether return address is next address of prefix 172.16.0.0/14. - final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2); - - // Check whether new downstream is next address of address 172.16.0.42/24. - final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3); - final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3, - new LinkAddress("172.16.0.1/24"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3); - verifyNotifyConflictAndRelease(mHotspotIpServer); - verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - - // Check whether return address is next address of prefix 172.16.1.42/24. - final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4); - final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4, - new LinkAddress("172.16.0.1/13"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4); - verifyNotifyConflictAndRelease(mHotspotIpServer); - verifyNotifyConflictAndRelease(mUsbIpServer); - - // Check whether return address is next address of prefix 172.16.0.1/13. - final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5); - // Check whether return address is next address of prefix 172.24.0.42/24. - final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6); - final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5, - new LinkAddress("172.24.0.1/12"), null, - makeNetworkCapabilities(TRANSPORT_CELLULAR)); - mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5); - verifyNotifyConflictAndRelease(mHotspotIpServer); - verifyNotifyConflictAndRelease(mUsbIpServer); - - // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42. - final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1); - // Check whether new downstream is next address of address 10.31.43.42/24. - final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer, - true /* useLastAddress */); - assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2); - } - - private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception { - verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.releaseDownstream(ipServer); - reset(ipServer); - setUpIpServers(); - } - - private int getSubAddress(final byte... ipv4Address) { - assertEquals(4, ipv4Address.length); - - int subnet = Byte.toUnsignedInt(ipv4Address[2]); - return (subnet << 8) + ipv4Address[3]; - } - - private void assertReseveredWifiP2pPrefix() throws Exception { - LinkAddress address = requestDownstreamAddress(mHotspotIpServer, - true /* useLastAddress */); - final IpPrefix hotspotPrefix = asIpPrefix(address); - final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress); - assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - } - - @Test - public void testEnableLegacyWifiP2PAddress() throws Exception { - when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( - getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); - // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix - // is resevered. - assertReseveredWifiP2pPrefix(); - - when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true); - assertReseveredWifiP2pPrefix(); - - // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address. - LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer, - true /* useLastAddress */); - assertEquals(mLegacyWifiP2pAddress, address); - mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java deleted file mode 100644 index 237e2c27bfa1..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.net.util.SharedLog; -import android.provider.DeviceConfig; -import android.telephony.TelephonyManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.test.BroadcastInterceptingContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -import java.util.Arrays; -import java.util.Iterator; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class TetheringConfigurationTest { - private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); - - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; - @Mock private Context mContext; - @Mock private TelephonyManager mTelephonyManager; - @Mock private Resources mResources; - @Mock private Resources mResourcesForSubId; - private Context mMockContext; - private boolean mHasTelephonyManager; - private boolean mEnableLegacyDhcpServer; - private MockitoSession mMockingSession; - - private class MockTetheringConfiguration extends TetheringConfiguration { - MockTetheringConfiguration(Context ctx, SharedLog log, int id) { - super(ctx, log, id); - } - - @Override - protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { - return mResourcesForSubId; - } - } - - private class MockContext extends BroadcastInterceptingContext { - MockContext(Context base) { - super(base); - } - - @Override - public Resources getResources() { - return mResources; - } - - @Override - public Object getSystemService(String name) { - if (Context.TELEPHONY_SERVICE.equals(name)) { - return mHasTelephonyManager ? mTelephonyManager : null; - } - return super.getSystemService(name); - } - } - - @Before - public void setUp() throws Exception { - // TODO: use a dependencies class instead of mock statics. - mMockingSession = mockitoSession() - .initMocks(this) - .mockStatic(DeviceConfig.class) - .strictness(Strictness.WARN) - .startMocking(); - doReturn(null).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( - new String[0]); - when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( - TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn( - new String[0]); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(new String[0]); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) - .thenReturn(false); - initializeBpfOffloadConfiguration(true, null /* unset */); - initEnableSelectAllPrefixRangeFlag(null /* unset */); - - mHasTelephonyManager = true; - mMockContext = new MockContext(mContext); - mEnableLegacyDhcpServer = false; - } - - @After - public void tearDown() throws Exception { - mMockingSession.finishMocking(); - } - - private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) { - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( - legacyTetherUpstreamTypes); - return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - } - - @Test - public void testNoTelephonyManagerMeansNoDun() { - mHasTelephonyManager = false; - final TetheringConfiguration cfg = getTetheringConfiguration( - new int[]{TYPE_MOBILE_DUN, TYPE_WIFI}); - assertFalse(cfg.isDunRequired); - assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); - // Just to prove we haven't clobbered Wi-Fi: - assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); - } - - @Test - public void testDunFromTelephonyManagerMeansDun() { - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true); - - final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); - final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI); - final TetheringConfiguration cfgWifiDun = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE_DUN); - final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN); - - for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, - cfgWifiDun, cfgMobileWifiHipriDun)) { - String msg = "config=" + cfg.toString(); - assertTrue(msg, cfg.isDunRequired); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); - assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - // Just to prove we haven't clobbered Wi-Fi: - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); - } - } - - @Test - public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); - final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI); - final TetheringConfiguration cfgWifiDun = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE_DUN); - final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE); - final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration( - TYPE_WIFI, TYPE_MOBILE_HIPRI); - final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration( - TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN); - - String msg; - // TYPE_MOBILE_DUN should be present in none of the combinations. - // TYPE_WIFI should not be affected. - for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun, - cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) { - msg = "config=" + cfg.toString(); - assertFalse(msg, cfg.isDunRequired); - assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); - } - - for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun, - cfgMobileWifiHipriDun)) { - msg = "config=" + cfg.toString(); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - } - msg = "config=" + cfgWifiMobile.toString(); - assertTrue(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertFalse(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - msg = "config=" + cfgWifiHipri.toString(); - assertFalse(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); - assertTrue(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); - - } - - @Test - public void testNoDefinedUpstreamTypesAddsEthernet() { - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{}); - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); - // The following is because the code always adds some kind of mobile - // upstream, be it DUN or, in this case where DUN is NOT required, - // make sure there is at least one of MOBILE or HIPRI. With the empty - // list of the configuration in this test, it will always add both - // MOBILE and HIPRI, in that order. - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); - assertFalse(upstreamIterator.hasNext()); - } - - @Test - public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( - new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); - assertFalse(upstreamIterator.hasNext()); - } - - @Test - public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { - when(mResources.getIntArray(R.array.config_tether_upstream_types)) - .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); - - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); - assertTrue(upstreamIterator.hasNext()); - assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); - assertFalse(upstreamIterator.hasNext()); - } - - private void initializeBpfOffloadConfiguration( - final boolean fromRes, final String fromDevConfig) { - when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes); - doReturn(fromDevConfig).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD))); - } - - @Test - public void testBpfOffloadEnabledByResource() { - initializeBpfOffloadConfiguration(true, null /* unset */); - final TetheringConfiguration enableByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByRes.isBpfOffloadEnabled()); - } - - @Test - public void testBpfOffloadEnabledByDeviceConfigOverride() { - for (boolean res : new boolean[]{true, false}) { - initializeBpfOffloadConfiguration(res, "true"); - final TetheringConfiguration enableByDevConOverride = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByDevConOverride.isBpfOffloadEnabled()); - } - } - - @Test - public void testBpfOffloadDisabledByResource() { - initializeBpfOffloadConfiguration(false, null /* unset */); - final TetheringConfiguration disableByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(disableByRes.isBpfOffloadEnabled()); - } - - @Test - public void testBpfOffloadDisabledByDeviceConfigOverride() { - for (boolean res : new boolean[]{true, false}) { - initializeBpfOffloadConfiguration(res, "false"); - final TetheringConfiguration disableByDevConOverride = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(disableByDevConOverride.isBpfOffloadEnabled()); - } - } - - @Test - public void testNewDhcpServerDisabled() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - true); - doReturn("false").when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - final TetheringConfiguration enableByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByRes.enableLegacyDhcpServer); - - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - doReturn("true").when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - final TetheringConfiguration enableByDevConfig = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(enableByDevConfig.enableLegacyDhcpServer); - } - - @Test - public void testNewDhcpServerEnabled() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - doReturn("false").when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); - - final TetheringConfiguration cfg = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - - assertFalse(cfg.enableLegacyDhcpServer); - } - - @Test - public void testOffloadIntervalByResource() { - final TetheringConfiguration intervalByDefault = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, - intervalByDefault.getOffloadPollInterval()); - - final int[] testOverrides = {0, 3000, -1}; - for (final int override : testOverrides) { - when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( - override); - final TetheringConfiguration overrideByRes = - new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertEquals(override, overrideByRes.getOffloadPollInterval()); - } - } - - @Test - public void testGetResourcesBySubId() { - setUpResourceForSubId(); - final TetheringConfiguration cfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(cfg.provisioningApp.length == 0); - final int anyValidSubId = 1; - final MockTetheringConfiguration mockCfg = - new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); - assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); - assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); - assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); - assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); - } - - private void setUpResourceForSubId() { - when(mResourcesForSubId.getStringArray( - R.array.config_tether_dhcp_range)).thenReturn(new String[0]); - when(mResourcesForSubId.getStringArray( - R.array.config_tether_usb_regexs)).thenReturn(new String[0]); - when(mResourcesForSubId.getStringArray( - R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); - when(mResourcesForSubId.getStringArray( - R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]); - when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn( - new int[0]); - when(mResourcesForSubId.getStringArray( - R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); - when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResourcesForSubId.getString( - R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); - } - - @Test - public void testEnableLegacyWifiP2PAddress() throws Exception { - final TetheringConfiguration defaultCfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp()); - - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) - .thenReturn(true); - final TetheringConfiguration testCfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp()); - } - - private void initEnableSelectAllPrefixRangeFlag(final String value) { - doReturn(value).when( - () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES))); - } - - @Test - public void testSelectAllPrefixRangeFlag() throws Exception { - // Test default value. - final TetheringConfiguration defaultCfg = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled()); - - // Test disable flag. - initEnableSelectAllPrefixRangeFlag("false"); - final TetheringConfiguration testDisable = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertFalse(testDisable.isSelectAllPrefixRangeEnabled()); - - // Test enable flag. - initEnableSelectAllPrefixRangeFlag("true"); - final TetheringConfiguration testEnable = new TetheringConfiguration( - mMockContext, mLog, INVALID_SUBSCRIPTION_ID); - assertTrue(testEnable.isSelectAllPrefixRangeEnabled()); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt deleted file mode 100644 index 75c819bb0ced..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering - -import android.app.Notification -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_IMMUTABLE -import android.content.Context -import android.content.Intent -import android.content.pm.ActivityInfo -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.content.pm.ResolveInfo -import android.content.res.Resources -import android.net.ConnectivityManager.TETHERING_WIFI -import android.net.NetworkCapabilities -import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING -import android.os.Handler -import android.os.HandlerThread -import android.os.Looper -import android.os.UserHandle -import android.provider.Settings -import android.telephony.TelephonyManager -import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.test.BroadcastInterceptingContext -import com.android.networkstack.tethering.TetheringNotificationUpdater.ACTION_DISABLE_TETHERING -import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE -import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM -import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID -import com.android.networkstack.tethering.TetheringNotificationUpdater.getSettingsPackageName -import com.android.testutils.waitForIdle -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.fail -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.MockitoAnnotations - -const val TEST_SUBID = 1 -const val WIFI_MASK = 1 shl TETHERING_WIFI -const val TEST_DISALLOW_TITLE = "Tether function is disallowed" -const val TEST_DISALLOW_MESSAGE = "Please contact your admin" -const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" -const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." -const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" -const val TEST_ROAMING_TITLE = "Hotspot is on" -const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming." - -@RunWith(AndroidJUnit4::class) -@SmallTest -class TetheringNotificationUpdaterTest { - // lateinit used here for mocks as they need to be reinitialized between each test and the test - // should crash if they are used before being initialized. - @Mock private lateinit var mockContext: Context - @Mock private lateinit var notificationManager: NotificationManager - @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var testResources: Resources - - // lateinit for these classes under test, as they should be reset to a different instance for - // every test but should always be initialized before use (or the test should crash). - private lateinit var context: TestContext - private lateinit var notificationUpdater: TetheringNotificationUpdater - - // Initializing the following members depends on initializing some of the mocks and - // is more logically done in setup(). - private lateinit var fakeTetheringThread: HandlerThread - - private val ROAMING_CAPABILITIES = NetworkCapabilities() - private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING) - private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general - private val TIMEOUT_MS = 500L - private val ACTIVITY_PENDING_INTENT = 0 - private val BROADCAST_PENDING_INTENT = 1 - - private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { - override fun createContextAsUser(user: UserHandle, flags: Int) = - if (user == UserHandle.ALL) mockContext else this - override fun getSystemService(name: String) = - if (name == Context.TELEPHONY_SERVICE) telephonyManager - else super.getSystemService(name) - } - - private inner class WrappedNotificationUpdater(c: Context, looper: Looper) - : TetheringNotificationUpdater(c, looper) { - override fun getResourcesForSubId(c: Context, subId: Int) = - if (subId == TEST_SUBID) testResources else super.getResourcesForSubId(c, subId) - } - - private fun setupResources() { - doReturn(5).`when`(testResources) - .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) - doReturn(true).`when`(testResources) - .getBoolean(R.bool.config_upstream_roaming_notification) - doReturn(TEST_DISALLOW_TITLE).`when`(testResources) - .getString(R.string.disable_tether_notification_title) - doReturn(TEST_DISALLOW_MESSAGE).`when`(testResources) - .getString(R.string.disable_tether_notification_message) - doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) - .getString(R.string.no_upstream_notification_title) - doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) - .getString(R.string.no_upstream_notification_message) - doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources) - .getString(R.string.no_upstream_notification_disable_button) - doReturn(TEST_ROAMING_TITLE).`when`(testResources) - .getString(R.string.upstream_roaming_notification_title) - doReturn(TEST_ROAMING_MESSAGE).`when`(testResources) - .getString(R.string.upstream_roaming_notification_message) - } - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - context = TestContext(InstrumentationRegistry.getInstrumentation().context) - doReturn(notificationManager).`when`(mockContext) - .getSystemService(Context.NOTIFICATION_SERVICE) - fakeTetheringThread = HandlerThread(this::class.java.simpleName) - fakeTetheringThread.start() - notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper) - setupResources() - } - - @After - fun tearDown() { - fakeTetheringThread.quitSafely() - } - - private fun verifyActivityPendingIntent(intent: Intent, flags: Int) { - // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add - // the flag in creating arguments). If the described PendingIntent does not already exist, - // getActivity() will return null instead of PendingIntent object. - val pi = PendingIntent.getActivity( - context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - intent, - flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE, - null /* options */) - assertNotNull("Activity PendingIntent with FLAG_IMMUTABLE does not exist.", pi) - } - - private fun verifyBroadcastPendingIntent(intent: Intent, flags: Int) { - // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add - // the flag in creating arguments). If the described PendingIntent does not already exist, - // getBroadcast() will return null instead of PendingIntent object. - val pi = PendingIntent.getBroadcast( - context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - intent, - flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE) - assertNotNull("Broadcast PendingIntent with FLAG_IMMUTABLE does not exist.", pi) - } - - private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) - private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) - - private fun verifyNotification( - iconId: Int, - title: String, - text: String, - id: Int, - intentSenderType: Int, - intent: Intent, - flags: Int - ) { - verify(notificationManager, never()).cancel(any(), eq(id)) - - val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) - verify(notificationManager, times(1)) - .notify(any(), eq(id), notificationCaptor.capture()) - - val notification = notificationCaptor.getValue() - assertEquals(iconId, notification.smallIcon.resId) - assertEquals(title, notification.title()) - assertEquals(text, notification.text()) - - when (intentSenderType) { - ACTIVITY_PENDING_INTENT -> verifyActivityPendingIntent(intent, flags) - BROADCAST_PENDING_INTENT -> verifyBroadcastPendingIntent(intent, flags) - } - - reset(notificationManager) - } - - private fun verifyNotificationCancelled( - notificationIds: List<Int>, - resetAfterVerified: Boolean = true - ) { - notificationIds.forEach { - verify(notificationManager, times(1)).cancel(any(), eq(it)) - } - if (resetAfterVerified) reset(notificationManager) - } - - @Test - fun testRestrictedNotification() { - val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(context.packageManager)) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - // Set test sub id. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // User restrictions on. Show restricted notification. - notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, - RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - - // User restrictions off. Clear notification. - notificationUpdater.tetheringRestrictionLifted() - verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID)) - - // No downstream. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyZeroInteractions(notificationManager) - - // User restrictions on again. Show restricted notification. - notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, - RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - } - - val MAX_BACKOFF_MS = 200L - /** - * Waits for all messages, including delayed ones, to be processed. - * - * This will wait until the handler has no more messages to be processed including - * delayed ones, or the timeout has expired. It uses an exponential backoff strategy - * to wait longer and longer to consume less CPU, with the max granularity being - * MAX_BACKOFF_MS. - * - * @return true if all messages have been processed including delayed ones, false if timeout - * - * TODO: Move this method to com.android.testutils.HandlerUtils.kt. - */ - private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) { - fun hasMatchingMessages() = - if (what == null) hasMessagesOrCallbacks() else hasMessages(what) - val expiry = System.currentTimeMillis() + timeoutMs - var delay = 5L - while (System.currentTimeMillis() < expiry && hasMatchingMessages()) { - // None of Handler, Looper, Message and MessageQueue expose any way to retrieve - // the time when the next (let alone the last) message will be processed, so - // short of examining the internals with reflection sleep() is the only solution. - Thread.sleep(delay) - delay = (delay * 2) - .coerceAtMost(expiry - System.currentTimeMillis()) - .coerceAtMost(MAX_BACKOFF_MS) - } - - val timeout = expiry - System.currentTimeMillis() - if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms") - waitForIdle(timeout) - } - - @Test - fun testNoUpstreamNotification() { - val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName) - - // Set test sub id. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // There is no upstream. Show no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // Same capabilities changed. Nothing happened. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - verifyZeroInteractions(notificationManager) - - // Upstream come back. Clear no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID)) - - // No upstream again. Show no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // No downstream. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream and home capabilities. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream - // again. Don't put up no upstream notification. - doReturn(-1).`when`(testResources) - .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID)) - } - - @Test - fun testGetResourcesForSubId() { - doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt()) - doReturn(1234).`when`(telephonyManager).getSimCarrierId() - doReturn("000000").`when`(telephonyManager).getSimOperator() - - val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId. - val config = context.resources.configuration - var res = notificationUpdater.getResourcesForSubId(context, subId) - assertEquals(config.mcc, res.configuration.mcc) - assertEquals(config.mnc, res.configuration.mnc) - - doReturn(VERIZON_CARRIER_ID).`when`(telephonyManager).getSimCarrierId() - res = notificationUpdater.getResourcesForSubId(context, subId) - assertEquals(config.mcc, res.configuration.mcc) - assertEquals(config.mnc, res.configuration.mnc) - - doReturn("20404").`when`(telephonyManager).getSimOperator() - res = notificationUpdater.getResourcesForSubId(context, subId) - assertEquals(311, res.configuration.mcc) - assertEquals(480, res.configuration.mnc) - } - - @Test - fun testRoamingNotification() { - val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName) - val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(context.packageManager)) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - // Set test sub id. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Upstream capabilities changed to roaming state. Show roaming notification. - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE, - ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - - // Same capabilities change. Nothing happened. - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyZeroInteractions(notificationManager) - - // Upstream capabilities changed to home state. Clear roaming notification. - notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID)) - - // Upstream capabilities changed to roaming state again. Show roaming notification. - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE, - ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE) - - // No upstream. Clear roaming notification and show no upstream notification. - notificationUpdater.onUpstreamCapabilitiesChanged(null) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // No downstream. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - - // Wifi downstream again. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) - verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) - verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, - NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent, - FLAG_IMMUTABLE) - - // Set R.bool.config_upstream_roaming_notification to false and change upstream - // network to roaming state again. No roaming notification. - doReturn(false).`when`(testResources) - .getBoolean(R.bool.config_upstream_roaming_notification) - notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) - verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - } - - @Test - fun testGetSettingsPackageName() { - val defaultSettingsPackageName = "com.android.settings" - val testSettingsPackageName = "com.android.test.settings" - val pm = mock(PackageManager::class.java) - doReturn(null).`when`(pm).resolveActivity(any(), anyInt()) - assertEquals(defaultSettingsPackageName, getSettingsPackageName(pm)) - - val resolveInfo = ResolveInfo().apply { - activityInfo = ActivityInfo().apply { - name = "test" - applicationInfo = ApplicationInfo().apply { - packageName = testSettingsPackageName - } - } - } - doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt()) - assertEquals(testSettingsPackageName, getSettingsPackageName(pm)) - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java deleted file mode 100644 index 7bba67b05f88..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.Manifest.permission.ACCESS_NETWORK_STATE; -import static android.Manifest.permission.TETHER_PRIVILEGED; -import static android.Manifest.permission.WRITE_SETTINGS; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.UiAutomation; -import android.content.Intent; -import android.net.IIntResultListener; -import android.net.ITetheringConnector; -import android.net.ITetheringEventCallback; -import android.net.TetheringRequestParcel; -import android.os.Bundle; -import android.os.Handler; -import android.os.ResultReceiver; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.rule.ServiceTestRule; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.tethering.MockTetheringService.MockTetheringConnector; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public final class TetheringServiceTest { - private static final String TEST_IFACE_NAME = "test_wlan0"; - private static final String TEST_CALLER_PKG = "com.android.shell"; - private static final String TEST_ATTRIBUTION_TAG = null; - @Mock private ITetheringEventCallback mITetheringEventCallback; - @Rule public ServiceTestRule mServiceTestRule; - private Tethering mTethering; - private Intent mMockServiceIntent; - private ITetheringConnector mTetheringConnector; - private UiAutomation mUiAutomation; - - private class TestTetheringResult extends IIntResultListener.Stub { - private int mResult = -1; // Default value that does not match any result code. - @Override - public void onResult(final int resultCode) { - mResult = resultCode; - } - - public void assertResult(final int expected) { - assertEquals(expected, mResult); - } - } - - private class MyResultReceiver extends ResultReceiver { - MyResultReceiver(Handler handler) { - super(handler); - } - private int mResult = -1; // Default value that does not match any result code. - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - mResult = resultCode; - } - - public void assertResult(int expected) { - assertEquals(expected, mResult); - } - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mUiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - mServiceTestRule = new ServiceTestRule(); - mMockServiceIntent = new Intent( - InstrumentationRegistry.getTargetContext(), - MockTetheringService.class); - final MockTetheringConnector mockConnector = - (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent); - mTetheringConnector = mockConnector.getTetheringConnector(); - final MockTetheringService service = mockConnector.getService(); - mTethering = service.getTethering(); - } - - @After - public void tearDown() throws Exception { - mServiceTestRule.unbindService(); - mUiAutomation.dropShellPermissionIdentity(); - } - - private interface TestTetheringCall { - void runTetheringCall(TestTetheringResult result) throws Exception; - } - - private void runAsNoPermission(final TestTetheringCall test) throws Exception { - runTetheringCall(test, new String[0]); - } - - private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception { - runTetheringCall(test, TETHER_PRIVILEGED); - } - - private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception { - runTetheringCall(test, ACCESS_NETWORK_STATE); - } - - private void runAsWriteSettings(final TestTetheringCall test) throws Exception { - runTetheringCall(test, WRITE_SETTINGS); - } - - private void runTetheringCall(final TestTetheringCall test, String... permissions) - throws Exception { - if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions); - try { - when(mTethering.isTetheringSupported()).thenReturn(true); - test.runTetheringCall(new TestTetheringResult()); - } finally { - mUiAutomation.dropShellPermissionIdentity(); - } - } - - private void verifyNoMoreInteractionsForTethering() { - verifyNoMoreInteractions(mTethering); - verifyNoMoreInteractions(mITetheringEventCallback); - reset(mTethering, mITetheringEventCallback); - } - - private void runTether(final TestTetheringResult result) throws Exception { - when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); - mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).tether(TEST_IFACE_NAME); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testTether() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runTether(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runTether(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runUnTether(final TestTetheringResult result) throws Exception { - when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); - mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).untether(TEST_IFACE_NAME); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testUntether() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runUnTether(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runUnTether(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runSetUsbTethering(final TestTetheringResult result) throws Exception { - when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); - mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).setUsbTethering(true /* enable */); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testSetUsbTethering() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runSetUsbTethering(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runSetUsbTethering(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - - } - - private void runStartTethering(final TestTetheringResult result, - final TetheringRequestParcel request) throws Exception { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).startTethering(eq(request), eq(result)); - } - - @Test - public void testStartTethering() throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - - runAsNoPermission((result) -> { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runStartTethering(result, request); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runStartTethering(result, request); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result) - throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - request.exemptFromEntitlementCheck = true; - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - } - - @Test - public void testFailToBypassEntitlementWithoutNeworkStackPermission() throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - request.exemptFromEntitlementCheck = true; - - runAsNoPermission((result) -> { - runStartTetheringAndVerifyNoPermission(result); - }); - - runAsTetherPrivileged((result) -> { - runStartTetheringAndVerifyNoPermission(result); - }); - - runAsWriteSettings((result) -> { - runStartTetheringAndVerifyNoPermission(result); - }); - } - - private void runStopTethering(final TestTetheringResult result) throws Exception { - mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).stopTethering(TETHERING_WIFI); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testStopTethering() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, - TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runStopTethering(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runStopTethering(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runRequestLatestTetheringEntitlementResult() throws Exception { - final MyResultReceiver result = new MyResultReceiver(null); - mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, - true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG); - verify(mTethering).isTetheringSupported(); - verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), - eq(result), eq(true) /* showEntitlementUi */); - } - - @Test - public void testRequestLatestTetheringEntitlementResult() throws Exception { - // Run as no permission. - final MyResultReceiver result = new MyResultReceiver(null); - mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, - true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractions(mTethering); - - runAsTetherPrivileged((none) -> { - runRequestLatestTetheringEntitlementResult(); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((none) -> { - runRequestLatestTetheringEntitlementResult(); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runRegisterTetheringEventCallback() throws Exception { - mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback)); - } - - @Test - public void testRegisterTetheringEventCallback() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mITetheringEventCallback).onCallbackStopped( - TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((none) -> { - runRegisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - - runAsAccessNetworkState((none) -> { - runRegisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runUnregisterTetheringEventCallback() throws Exception { - mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mTethering).unregisterTetheringEventCallback(eq(mITetheringEventCallback)); - } - - @Test - public void testUnregisterTetheringEventCallback() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback, - TEST_CALLER_PKG); - verify(mITetheringEventCallback).onCallbackStopped( - TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((none) -> { - runUnregisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - - runAsAccessNetworkState((none) -> { - runUnregisterTetheringEventCallback(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runStopAllTethering(final TestTetheringResult result) throws Exception { - mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - verify(mTethering).untetherAll(); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testStopAllTethering() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runStopAllTethering(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runStopAllTethering(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } - - private void runIsTetheringSupported(final TestTetheringResult result) throws Exception { - mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); - verify(mTethering).isTetheringSupported(); - result.assertResult(TETHER_ERROR_NO_ERROR); - } - - @Test - public void testIsTetheringSupported() throws Exception { - runAsNoPermission((result) -> { - mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering).isTetherProvisioningRequired(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); - }); - - runAsTetherPrivileged((result) -> { - runIsTetheringSupported(result); - verifyNoMoreInteractionsForTethering(); - }); - - runAsWriteSettings((result) -> { - runIsTetheringSupported(result); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); - }); - } -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java deleted file mode 100644 index 114cb7ca6ec7..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ /dev/null @@ -1,2019 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.content.pm.PackageManager.GET_ACTIVITIES; -import static android.hardware.usb.UsbManager.USB_CONFIGURED; -import static android.hardware.usb.UsbManager.USB_CONNECTED; -import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; -import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; -import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; -import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; -import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; -import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; -import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; -import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_NCM; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; -import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; -import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; - -import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; -import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE; -import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.bluetooth.BluetoothAdapter; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.hardware.usb.UsbManager; -import android.net.ConnectivityManager; -import android.net.EthernetManager; -import android.net.EthernetManager.TetheredInterfaceCallback; -import android.net.EthernetManager.TetheredInterfaceRequest; -import android.net.IIntResultListener; -import android.net.INetd; -import android.net.ITetheringEventCallback; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.RouteInfo; -import android.net.TetherStatesParcel; -import android.net.TetheredClient; -import android.net.TetheringCallbackStartedParcel; -import android.net.TetheringConfigurationParcel; -import android.net.TetheringRequestParcel; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.IDhcpServer; -import android.net.ip.DadProxy; -import android.net.ip.IpNeighborMonitor; -import android.net.ip.IpServer; -import android.net.ip.RouterAdvertisementDaemon; -import android.net.util.InterfaceParams; -import android.net.util.NetworkConstants; -import android.net.util.SharedLog; -import android.net.wifi.SoftApConfiguration; -import android.net.wifi.WifiManager; -import android.net.wifi.p2p.WifiP2pGroup; -import android.net.wifi.p2p.WifiP2pInfo; -import android.net.wifi.p2p.WifiP2pManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.PersistableBundle; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.test.TestLooper; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.test.mock.MockContentResolver; - -import androidx.annotation.NonNull; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.StateMachine; -import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.internal.util.test.FakeSettingsProvider; -import com.android.testutils.MiscAsserts; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Vector; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class TetheringTest { - private static final int IFINDEX_OFFSET = 100; - - private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; - private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; - private static final String TEST_USB_IFNAME = "test_rndis0"; - private static final String TEST_WIFI_IFNAME = "test_wlan0"; - private static final String TEST_WLAN_IFNAME = "test_wlan1"; - private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; - private static final String TEST_NCM_IFNAME = "test_ncm0"; - private static final String TEST_ETH_IFNAME = "test_eth0"; - private static final String TEST_BT_IFNAME = "test_pan0"; - private static final String TETHERING_NAME = "Tethering"; - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - - private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; - - @Mock private ApplicationInfo mApplicationInfo; - @Mock private Context mContext; - @Mock private NetworkStatsManager mStatsManager; - @Mock private OffloadHardwareInterface mOffloadHardwareInterface; - @Mock private OffloadHardwareInterface.ForwardedStats mForwardedStats; - @Mock private Resources mResources; - @Mock private TelephonyManager mTelephonyManager; - @Mock private UsbManager mUsbManager; - @Mock private WifiManager mWifiManager; - @Mock private CarrierConfigManager mCarrierConfigManager; - @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; - @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; - @Mock private DadProxy mDadProxy; - @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; - @Mock private IpNeighborMonitor mIpNeighborMonitor; - @Mock private IDhcpServer mDhcpServer; - @Mock private INetd mNetd; - @Mock private UserManager mUserManager; - @Mock private NetworkRequest mNetworkRequest; - @Mock private ConnectivityManager mCm; - @Mock private EthernetManager mEm; - @Mock private TetheringNotificationUpdater mNotificationUpdater; - @Mock private BpfCoordinator mBpfCoordinator; - @Mock private PackageManager mPackageManager; - - private final MockIpServerDependencies mIpServerDependencies = - spy(new MockIpServerDependencies()); - private final MockTetheringDependencies mTetheringDependencies = - new MockTetheringDependencies(); - - // Like so many Android system APIs, these cannot be mocked because it is marked final. - // We have to use the real versions. - private final PersistableBundle mCarrierConfig = new PersistableBundle(); - private final TestLooper mLooper = new TestLooper(); - - private Vector<Intent> mIntents; - private BroadcastInterceptingContext mServiceContext; - private MockContentResolver mContentResolver; - private BroadcastReceiver mBroadcastReceiver; - private Tethering mTethering; - private PhoneStateListener mPhoneStateListener; - private InterfaceConfigurationParcel mInterfaceConfiguration; - private TetheringConfiguration mConfig; - private EntitlementManager mEntitleMgr; - private OffloadController mOffloadCtrl; - private PrivateAddressCoordinator mPrivateAddressCoordinator; - - private class TestContext extends BroadcastInterceptingContext { - TestContext(Context base) { - super(base); - } - - @Override - public ApplicationInfo getApplicationInfo() { - return mApplicationInfo; - } - - @Override - public ContentResolver getContentResolver() { - return mContentResolver; - } - - @Override - public String getPackageName() { - return "TetheringTest"; - } - - @Override - public Resources getResources() { - return mResources; - } - - @Override - public Object getSystemService(String name) { - if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; - if (Context.USB_SERVICE.equals(name)) return mUsbManager; - if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; - if (Context.USER_SERVICE.equals(name)) return mUserManager; - if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; - if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; - if (Context.ETHERNET_SERVICE.equals(name)) return mEm; - return super.getSystemService(name); - } - - @Override - public PackageManager getPackageManager() { - return mPackageManager; - } - - @Override - public String getSystemServiceName(Class<?> serviceClass) { - if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; - return super.getSystemServiceName(serviceClass); - } - } - - public class MockIpServerDependencies extends IpServer.Dependencies { - @Override - public DadProxy getDadProxy( - Handler handler, InterfaceParams ifParams) { - return mDadProxy; - } - - @Override - public RouterAdvertisementDaemon getRouterAdvertisementDaemon( - InterfaceParams ifParams) { - return mRouterAdvertisementDaemon; - } - - @Override - public InterfaceParams getInterfaceParams(String ifName) { - assertTrue("Non-mocked interface " + ifName, - ifName.equals(TEST_USB_IFNAME) - || ifName.equals(TEST_WLAN_IFNAME) - || ifName.equals(TEST_MOBILE_IFNAME) - || ifName.equals(TEST_P2P_IFNAME) - || ifName.equals(TEST_NCM_IFNAME) - || ifName.equals(TEST_ETH_IFNAME)); - final String[] ifaces = new String[] { - TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME, TEST_ETH_IFNAME}; - return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, - MacAddress.ALL_ZEROS_ADDRESS); - } - - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { - new Thread(() -> { - try { - cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); - } catch (RemoteException e) { - fail(e.getMessage()); - } - }).run(); - } - - public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l, - IpNeighborMonitor.NeighborEventConsumer c) { - return mIpNeighborMonitor; - } - } - - // MyTetheringConfiguration is used to override static method for testing. - private class MyTetheringConfiguration extends TetheringConfiguration { - MyTetheringConfiguration(Context ctx, SharedLog log, int id) { - super(ctx, log, id); - } - - @Override - protected String getDeviceConfigProperty(final String name) { - return null; - } - - @Override - protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { - return mResources; - } - } - - public class MockTetheringDependencies extends TetheringDependencies { - StateMachine mUpstreamNetworkMonitorSM; - ArrayList<IpServer> mIpv6CoordinatorNotifyList; - - public void reset() { - mUpstreamNetworkMonitorSM = null; - mIpv6CoordinatorNotifyList = null; - } - - @Override - public BpfCoordinator getBpfCoordinator( - BpfCoordinator.Dependencies deps) { - return mBpfCoordinator; - } - - @Override - public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { - return mOffloadHardwareInterface; - } - - @Override - public OffloadController getOffloadController(Handler h, SharedLog log, - OffloadController.Dependencies deps) { - mOffloadCtrl = spy(super.getOffloadController(h, log, deps)); - // Return real object here instead of mock because - // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object. - return mOffloadCtrl; - } - - @Override - public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, - StateMachine target, SharedLog log, int what) { - mUpstreamNetworkMonitorSM = target; - return mUpstreamNetworkMonitor; - } - - @Override - public IPv6TetheringCoordinator getIPv6TetheringCoordinator( - ArrayList<IpServer> notifyList, SharedLog log) { - mIpv6CoordinatorNotifyList = notifyList; - return mIPv6TetheringCoordinator; - } - - @Override - public IpServer.Dependencies getIpServerDependencies() { - return mIpServerDependencies; - } - - @Override - public NetworkRequest getDefaultNetworkRequest() { - return mNetworkRequest; - } - - @Override - public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log, - Runnable callback) { - mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback)); - return mEntitleMgr; - } - - @Override - public boolean isTetheringSupported() { - return true; - } - - @Override - public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, - int subId) { - mConfig = spy(new MyTetheringConfiguration(ctx, log, subId)); - return mConfig; - } - - @Override - public INetd getINetd(Context context) { - return mNetd; - } - - @Override - public Looper getTetheringLooper() { - return mLooper.getLooper(); - } - - @Override - public Context getContext() { - return mServiceContext; - } - - @Override - public BluetoothAdapter getBluetoothAdapter() { - // TODO: add test for bluetooth tethering. - return null; - } - - @Override - public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) { - return mNotificationUpdater; - } - - @Override - public boolean isTetheringDenied() { - return false; - } - - - @Override - public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx, - TetheringConfiguration cfg) { - mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg); - return mPrivateAddressCoordinator; - } - } - - private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, - boolean withIPv6, boolean with464xlat) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_MOBILE_IFNAME); - - if (withIPv4) { - prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("10.0.0.1"), - TEST_MOBILE_IFNAME, RTN_UNICAST)); - } - - if (withIPv6) { - prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2")); - prop.addLinkAddress( - new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"), - NetworkConstants.RFC7421_PREFIX_LENGTH)); - prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), - InetAddresses.parseNumericAddress("2001:db8::1"), - TEST_MOBILE_IFNAME, RTN_UNICAST)); - } - - if (with464xlat) { - final LinkProperties stackedLink = new LinkProperties(); - stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME); - stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("192.0.0.1"), - TEST_XLAT_MOBILE_IFNAME, RTN_UNICAST)); - - prop.addStackedLink(stackedLink); - } - - - final NetworkCapabilities capabilities = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - return new UpstreamNetworkState(prop, capabilities, new Network(100)); - } - - private static UpstreamNetworkState buildMobileIPv4UpstreamState() { - return buildMobileUpstreamState(true, false, false); - } - - private static UpstreamNetworkState buildMobileIPv6UpstreamState() { - return buildMobileUpstreamState(false, true, false); - } - - private static UpstreamNetworkState buildMobileDualStackUpstreamState() { - return buildMobileUpstreamState(true, true, false); - } - - private static UpstreamNetworkState buildMobile464xlatUpstreamState() { - return buildMobileUpstreamState(false, true, true); - } - - // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and - // after use. - @BeforeClass - public static void setupOnce() { - FakeSettingsProvider.clearSettingsProvider(); - } - - @AfterClass - public static void tearDownOnce() { - FakeSettingsProvider.clearSettingsProvider(); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mResources.getStringArray(R.array.config_tether_dhcp_range)) - .thenReturn(new String[0]); - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - false); - when(mNetd.interfaceGetList()) - .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME, TEST_ETH_IFNAME}); - when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); - mInterfaceConfiguration = new InterfaceConfigurationParcel(); - mInterfaceConfiguration.flags = new String[0]; - when(mRouterAdvertisementDaemon.start()) - .thenReturn(true); - initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */, - 0 /* defaultDisabled */); - when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats); - - mServiceContext = new TestContext(mContext); - mContentResolver = new MockContentResolver(mServiceContext); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - setTetheringSupported(true /* supported */); - mIntents = new Vector<>(); - mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mIntents.addElement(intent); - } - }; - mServiceContext.registerReceiver(mBroadcastReceiver, - new IntentFilter(ACTION_TETHER_STATE_CHANGED)); - mTethering = makeTethering(); - verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); - verify(mNetd).registerUnsolicitedEventListener(any()); - final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor = - ArgumentCaptor.forClass(PhoneStateListener.class); - verify(mTelephonyManager).listen(phoneListenerCaptor.capture(), - eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)); - verify(mWifiManager).registerSoftApCallback(any(), any()); - mPhoneStateListener = phoneListenerCaptor.getValue(); - } - - private void setTetheringSupported(final boolean supported) { - Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, - supported ? 1 : 0); - when(mUserManager.hasUserRestriction( - UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported); - // Setup tetherable configuration. - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) - .thenReturn(new String[] { "test_ncm\\d" }); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); - } - - private void initTetheringUpstream(UpstreamNetworkState upstreamState) { - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); - } - - private Tethering makeTethering() { - mTetheringDependencies.reset(); - return new Tethering(mTetheringDependencies); - } - - private TetheringRequestParcel createTetheringRequestParcel(final int type) { - return createTetheringRequestParcel(type, null, null, false); - } - - private TetheringRequestParcel createTetheringRequestParcel(final int type, - final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = type; - request.localIPv4Address = serverAddr; - request.staticClientAddress = clientAddr; - request.exemptFromEntitlementCheck = exempt; - request.showProvisioningUi = false; - - return request; - } - - @After - public void tearDown() { - mServiceContext.unregisterReceiver(mBroadcastReceiver); - } - - private void sendWifiApStateChanged(int state) { - final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - intent.putExtra(EXTRA_WIFI_AP_STATE, state); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private void sendWifiApStateChanged(int state, String ifname, int ipmode) { - final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - intent.putExtra(EXTRA_WIFI_AP_STATE, state); - intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); - intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = { - android.Manifest.permission.ACCESS_FINE_LOCATION, - android.Manifest.permission.ACCESS_WIFI_STATE - }; - - private void sendWifiP2pConnectionChanged( - boolean isGroupFormed, boolean isGroupOwner, String ifname) { - WifiP2pGroup group = null; - WifiP2pInfo p2pInfo = new WifiP2pInfo(); - p2pInfo.groupFormed = isGroupFormed; - if (isGroupFormed) { - p2pInfo.isGroupOwner = isGroupOwner; - group = mock(WifiP2pGroup.class); - when(group.isGroupOwner()).thenReturn(isGroupOwner); - when(group.getInterface()).thenReturn(ifname); - } - - final Intent intent = mock(Intent.class); - when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo); - when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group); - - mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, - P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); - } - - private void sendUsbBroadcast(boolean connected, boolean configured, boolean function, - int type) { - final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); - intent.putExtra(USB_CONNECTED, connected); - intent.putExtra(USB_CONFIGURED, configured); - if (type == TETHERING_USB) { - intent.putExtra(USB_FUNCTION_RNDIS, function); - } else { - intent.putExtra(USB_FUNCTION_NCM, function); - } - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private void sendConfigurationChanged() { - final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); - mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - - private void verifyInterfaceServingModeStarted(String ifname) throws Exception { - verify(mNetd, times(1)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherInterfaceAdd(ifname); - verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, ifname); - verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname), - anyString(), anyString()); - } - - private void verifyTetheringBroadcast(String ifname, String whichExtra) { - // Verify that ifname is in the whichExtra array of the tether state changed broadcast. - final Intent bcast = mIntents.get(0); - assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction()); - final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra); - assertTrue(ifnames.contains(ifname)); - mIntents.remove(bcast); - } - - public void failingLocalOnlyHotspotLegacyApBroadcast( - boolean emulateInterfaceStatusChanged) throws Exception { - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // hotspot mode is to be started. - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - } - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); - mLooper.dispatchAll(); - - // If, and only if, Tethering received an interface status changed then - // it creates a IpServer and sends out a broadcast indicating that the - // interface is "available". - if (emulateInterfaceStatusChanged) { - // There is 1 IpServer state change event: STATE_AVAILABLE - verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - } - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - } - - private void prepareNcmTethering() { - // Emulate startTethering(TETHERING_NCM) called - mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null); - mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); - - mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true); - } - - private void prepareUsbTethering(UpstreamNetworkState upstreamState) { - initTetheringUpstream(upstreamState); - - // Emulate pressing the USB tethering button in Settings UI. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); - mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - } - - @Test - public void testUsbConfiguredBroadcastStartsTethering() throws Exception { - UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - prepareUsbTethering(upstreamState); - - // This should produce no activity of any kind. - verifyNoMoreInteractions(mNetd); - - // Pretend we then receive USB configured broadcast. - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - // Now we should see the start of tethering mechanics (in this case: - // tetherMatchingInterfaces() which starts by fetching all interfaces). - verify(mNetd, times(1)).interfaceGetList(); - - // UpstreamNetworkMonitor should receive selected upstream - verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); - verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); - } - - @Test - public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception { - failingLocalOnlyHotspotLegacyApBroadcast(true); - } - - @Test - public void failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged() throws Exception { - failingLocalOnlyHotspotLegacyApBroadcast(false); - } - - public void workingLocalOnlyHotspotEnrichedApBroadcast( - boolean emulateInterfaceStatusChanged) throws Exception { - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // hotspot mode is to be started. - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - } - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY); - mLooper.dispatchAll(); - - verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, times(1)).tetherStartWithConfiguration(any()); - verifyNoMoreInteractions(mNetd); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); - verifyNoMoreInteractions(mWifiManager); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); - verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY - verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); - - // Emulate externally-visible WifiManager effects, when hotspot mode - // is being torn down. - sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mTethering.interfaceRemoved(TEST_WLAN_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - // interfaceSetCfg() called once for enabling and twice disabling IPv4. - verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherStop(); - verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); - verify(mWifiManager, times(3)).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); - } - - /** - * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator. - */ - private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) { - // IPv6TetheringCoordinator must have been notified of downstream - verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream( - argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)), - eq(IpServer.STATE_TETHERED)); - - for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) { - UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false); - ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, - upstreamState.linkProperties.isIpv6Provisioned() - ? ipv6OnlyState.linkProperties - : null); - } - mLooper.dispatchAll(); - } - - private void runUsbTethering(UpstreamNetworkState upstreamState) { - prepareUsbTethering(upstreamState); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - } - - @Test - public void workingMobileUsbTethering_IPv4() throws Exception { - UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - sendIPv6TetherUpdates(upstreamState); - verify(mDadProxy, never()).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - } - - @Test - public void workingMobileUsbTethering_IPv4LegacyDhcp() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - true); - sendConfigurationChanged(); - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - runUsbTethering(upstreamState); - sendIPv6TetherUpdates(upstreamState); - - verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); - } - - @Test - public void workingMobileUsbTethering_IPv6() throws Exception { - UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - sendIPv6TetherUpdates(upstreamState); - // TODO: add interfaceParams to compare in verify. - verify(mDadProxy, times(1)).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - } - - @Test - public void workingMobileUsbTethering_DualStack() throws Exception { - UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mRouterAdvertisementDaemon, times(1)).start(); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - - sendIPv6TetherUpdates(upstreamState); - verify(mDadProxy, times(1)).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - } - - @Test - public void workingMobileUsbTethering_MultipleUpstreams() throws Exception { - UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - sendIPv6TetherUpdates(upstreamState); - verify(mDadProxy, times(1)).setUpstreamIface(notNull()); - verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - } - - @Test - public void workingMobileUsbTethering_v6Then464xlat() throws Exception { - // Setup IPv6 - UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); - runUsbTethering(upstreamState); - - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - - // Then 464xlat comes up - upstreamState = buildMobile464xlatUpstreamState(); - initTetheringUpstream(upstreamState); - - // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. - mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage( - Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, - 0, - upstreamState); - mLooper.dispatchAll(); - - // Forwarding is added for 464xlat - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - // Forwarding was not re-added for v6 (still times(1)) - verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - // DHCP not restarted on downstream (still times(1)) - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - } - - @Test - public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception { - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); - sendConfigurationChanged(); - - // Setup IPv6 - final UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); - runUsbTethering(upstreamState); - - // UpstreamNetworkMonitor should choose upstream automatically - // (in this specific case: choose the default network). - verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); - verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); - - verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); - } - - private void runNcmTethering() { - prepareNcmTethering(); - sendUsbBroadcast(true, true, true, TETHERING_NCM); - mLooper.dispatchAll(); - } - - @Test - public void workingNcmTethering() throws Exception { - runNcmTethering(); - - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - } - - @Test - public void workingNcmTethering_LegacyDhcp() { - when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( - true); - sendConfigurationChanged(); - runNcmTethering(); - - verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); - } - - @Test - public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { - workingLocalOnlyHotspotEnrichedApBroadcast(true); - } - - @Test - public void workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged() throws Exception { - workingLocalOnlyHotspotEnrichedApBroadcast(false); - } - - // TODO: Test with and without interfaceStatusChanged(). - @Test - public void failingWifiTetheringLegacyApBroadcast() throws Exception { - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - - // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startTetheredHotspot(null); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // tethering mode is to be started. - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); - mLooper.dispatchAll(); - - // There is 1 IpServer state change event: STATE_AVAILABLE - verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - } - - // TODO: Test with and without interfaceStatusChanged(). - @Test - public void workingWifiTetheringEnrichedApBroadcast() throws Exception { - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - - // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startTetheredHotspot(null); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // tethering mode is to be started. - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); - - verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, times(1)).tetherStartWithConfiguration(any()); - verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), - anyString(), anyString()); - verifyNoMoreInteractions(mNetd); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); - verifyNoMoreInteractions(mWifiManager); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER); - verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // In tethering mode, in the default configuration, an explicit request - // for a mobile network is also made. - verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest(); - // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED - verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); - verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI)); - - ///// - // We do not currently emulate any upstream being found. - // - // This is why there are no calls to verify mNetd.tetherAddForward() or - // mNetd.ipfwdAddInterfaceForward(). - ///// - - // Emulate pressing the WiFi tethering button. - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).stopSoftAp(); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, when tethering mode - // is being torn down. - sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mTethering.interfaceRemoved(TEST_WLAN_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - // interfaceSetCfg() called once for enabling and twice for disabling IPv4. - verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherStop(); - verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); - verify(mWifiManager, times(3)).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNetd); - verifyNoMoreInteractions(mWifiManager); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME)); - } - - // TODO: Test with and without interfaceStatusChanged(). - @Test - public void failureEnablingIpForwarding() throws Exception { - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); - - // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - mLooper.dispatchAll(); - verify(mWifiManager, times(1)).startTetheredHotspot(null); - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - - // Emulate externally-visible WifiManager effects, causing the - // per-interface state machine to start up, and telling us that - // tethering mode is to be started. - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); - - // We verify get/set called three times here: twice for setup and once during - // teardown because all events happen over the course of the single - // dispatchAll() above. Note that once the IpServer IPv4 address config - // code is refactored the two calls during shutdown will revert to one. - verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); - verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), - anyString(), anyString()); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); - // There are 3 IpServer state change event: - // STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE. - verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); - verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI)); - verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - // This is called, but will throw. - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - // This never gets called because of the exception thrown above. - verify(mNetd, times(0)).tetherStartWithConfiguration(any()); - // When the main state machine transitions to an error state it tells - // downstream interfaces, which causes us to tell Wi-Fi about the error - // so it can take down AP mode. - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); - verify(mWifiManager).updateInterfaceIpState( - TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); - - verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNetd); - } - - private void runUserRestrictionsChange( - boolean currentDisallow, boolean nextDisallow, boolean isTetheringActive, - int expectedInteractionsWithShowNotification) throws Exception { - final Bundle newRestrictions = new Bundle(); - newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); - final Tethering mockTethering = mock(Tethering.class); - when(mockTethering.isTetheringActive()).thenReturn(isTetheringActive); - when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); - - final Tethering.UserRestrictionActionListener ural = - new Tethering.UserRestrictionActionListener( - mUserManager, mockTethering, mNotificationUpdater); - ural.mDisallowTethering = currentDisallow; - - ural.onUserRestrictionsChanged(); - - verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) - .notifyTetheringDisabledByRestriction(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); - } - - @Test - public void testDisallowTetheringWhenTetheringIsNotActive() throws Exception { - final boolean isTetheringActive = false; - final boolean currDisallow = false; - final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 0; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testDisallowTetheringWhenTetheringIsActive() throws Exception { - final boolean isTetheringActive = true; - final boolean currDisallow = false; - final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 1; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testAllowTetheringWhenTetheringIsNotActive() throws Exception { - final boolean isTetheringActive = false; - final boolean currDisallow = true; - final boolean nextDisallow = false; - final int expectedInteractionsWithShowNotification = 0; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testAllowTetheringWhenTetheringIsActive() throws Exception { - final boolean isTetheringActive = true; - final boolean currDisallow = true; - final boolean nextDisallow = false; - final int expectedInteractionsWithShowNotification = 0; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - @Test - public void testDisallowTetheringUnchanged() throws Exception { - final boolean isTetheringActive = true; - final int expectedInteractionsWithShowNotification = 0; - boolean currDisallow = true; - boolean nextDisallow = true; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - - currDisallow = false; - nextDisallow = false; - - runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, - expectedInteractionsWithShowNotification); - } - - private class TestTetheringEventCallback extends ITetheringEventCallback.Stub { - private final ArrayList<Network> mActualUpstreams = new ArrayList<>(); - private final ArrayList<TetheringConfigurationParcel> mTetheringConfigs = - new ArrayList<>(); - private final ArrayList<TetherStatesParcel> mTetherStates = new ArrayList<>(); - private final ArrayList<Integer> mOffloadStatus = new ArrayList<>(); - - // This function will remove the recorded callbacks, so it must be called once for - // each callback. If this is called after multiple callback, the order matters. - // onCallbackCreated counts as the first call to expectUpstreamChanged with - // @see onCallbackCreated. - public void expectUpstreamChanged(Network... networks) { - if (networks == null) { - assertNoUpstreamChangeCallback(); - return; - } - - final ArrayList<Network> expectedUpstreams = - new ArrayList<Network>(Arrays.asList(networks)); - for (Network upstream : expectedUpstreams) { - // throws OOB if no expectations - assertEquals(mActualUpstreams.remove(0), upstream); - } - assertNoUpstreamChangeCallback(); - } - - // This function will remove the recorded callbacks, so it must be called once - // for each callback. If this is called after multiple callback, the order matters. - // onCallbackCreated counts as the first call to onConfigurationChanged with - // @see onCallbackCreated. - public void expectConfigurationChanged(TetheringConfigurationParcel... tetherConfigs) { - final ArrayList<TetheringConfigurationParcel> expectedTetherConfig = - new ArrayList<TetheringConfigurationParcel>(Arrays.asList(tetherConfigs)); - for (TetheringConfigurationParcel config : expectedTetherConfig) { - // throws OOB if no expectations - final TetheringConfigurationParcel actualConfig = mTetheringConfigs.remove(0); - assertTetherConfigParcelEqual(actualConfig, config); - } - assertNoConfigChangeCallback(); - } - - public void expectOffloadStatusChanged(final int expectedStatus) { - assertOffloadStatusChangedCallback(); - assertEquals(mOffloadStatus.remove(0), new Integer(expectedStatus)); - } - - public TetherStatesParcel pollTetherStatesChanged() { - assertStateChangeCallback(); - return mTetherStates.remove(0); - } - - @Override - public void onUpstreamChanged(Network network) { - mActualUpstreams.add(network); - } - - @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { - mTetheringConfigs.add(config); - } - - @Override - public void onTetherStatesChanged(TetherStatesParcel states) { - mTetherStates.add(states); - } - - @Override - public void onTetherClientsChanged(List<TetheredClient> clients) { - // TODO: check this - } - - @Override - public void onOffloadStatusChanged(final int status) { - mOffloadStatus.add(status); - } - - @Override - public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { - mActualUpstreams.add(parcel.upstreamNetwork); - mTetheringConfigs.add(parcel.config); - mTetherStates.add(parcel.states); - mOffloadStatus.add(parcel.offloadStatus); - } - - @Override - public void onCallbackStopped(int errorCode) { } - - public void assertNoUpstreamChangeCallback() { - assertTrue(mActualUpstreams.isEmpty()); - } - - public void assertNoConfigChangeCallback() { - assertTrue(mTetheringConfigs.isEmpty()); - } - - public void assertNoStateChangeCallback() { - assertTrue(mTetherStates.isEmpty()); - } - - public void assertStateChangeCallback() { - assertFalse(mTetherStates.isEmpty()); - } - - public void assertOffloadStatusChangedCallback() { - assertFalse(mOffloadStatus.isEmpty()); - } - - public void assertNoCallback() { - assertNoUpstreamChangeCallback(); - assertNoConfigChangeCallback(); - assertNoStateChangeCallback(); - } - - private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual, - @NonNull TetheringConfigurationParcel expect) { - assertEquals(actual.subId, expect.subId); - assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs); - assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs); - assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs); - assertEquals(actual.isDunRequired, expect.isDunRequired); - assertEquals(actual.chooseUpstreamAutomatically, expect.chooseUpstreamAutomatically); - assertArrayEquals(actual.preferredUpstreamIfaceTypes, - expect.preferredUpstreamIfaceTypes); - assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges); - assertArrayEquals(actual.defaultIPv4DNS, expect.defaultIPv4DNS); - assertEquals(actual.enableLegacyDhcpServer, expect.enableLegacyDhcpServer); - assertArrayEquals(actual.provisioningApp, expect.provisioningApp); - assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi); - assertEquals(actual.provisioningCheckPeriod, expect.provisioningCheckPeriod); - } - } - - private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) { - assertFalse(parcel == null); - assertEquals(0, parcel.availableList.length); - assertEquals(0, parcel.tetheredList.length); - assertEquals(0, parcel.localOnlyList.length); - assertEquals(0, parcel.erroredIfaceList.length); - assertEquals(0, parcel.lastErrorList.length); - MiscAsserts.assertFieldCountEquals(5, TetherStatesParcel.class); - } - - @Test - public void testRegisterTetheringEventCallback() throws Exception { - TestTetheringEventCallback callback = new TestTetheringEventCallback(); - TestTetheringEventCallback callback2 = new TestTetheringEventCallback(); - - // 1. Register one callback before running any tethering. - mTethering.registerTetheringEventCallback(callback); - mLooper.dispatchAll(); - callback.expectUpstreamChanged(new Network[] {null}); - callback.expectConfigurationChanged( - mTethering.getTetheringConfiguration().toStableParcelable()); - TetherStatesParcel tetherState = callback.pollTetherStatesChanged(); - assertTetherStatesNotNullButEmpty(tetherState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - // 2. Enable wifi tethering. - UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - initTetheringUpstream(upstreamState); - when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); - mLooper.dispatchAll(); - tetherState = callback.pollTetherStatesChanged(); - assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - - mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); - tetherState = callback.pollTetherStatesChanged(); - assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); - callback.expectUpstreamChanged(upstreamState.network); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED); - - // 3. Register second callback. - mTethering.registerTetheringEventCallback(callback2); - mLooper.dispatchAll(); - callback2.expectUpstreamChanged(upstreamState.network); - callback2.expectConfigurationChanged( - mTethering.getTetheringConfiguration().toStableParcelable()); - tetherState = callback2.pollTetherStatesChanged(); - assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); - callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED); - - // 4. Unregister first callback and disable wifi tethering - mTethering.unregisterTetheringEventCallback(callback); - mLooper.dispatchAll(); - mTethering.stopTethering(TETHERING_WIFI); - sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mLooper.dispatchAll(); - tetherState = callback2.pollTetherStatesChanged(); - assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - mLooper.dispatchAll(); - callback2.expectUpstreamChanged(new Network[] {null}); - callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - callback.assertNoCallback(); - } - - @Test - public void testReportFailCallbackIfOffloadNotSupported() throws Exception { - final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - TestTetheringEventCallback callback = new TestTetheringEventCallback(); - mTethering.registerTetheringEventCallback(callback); - mLooper.dispatchAll(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - - // 1. Offload fail if no OffloadConfig. - initOffloadConfiguration(false /* offloadConfig */, true /* offloadControl */, - 0 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - reset(mUsbManager); - // 2. Offload fail if no OffloadControl. - initOffloadConfiguration(true /* offloadConfig */, false /* offloadControl */, - 0 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - reset(mUsbManager); - // 3. Offload fail if disabled by settings. - initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */, - 1 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - } - - private void runStopUSBTethering() { - mTethering.stopTethering(TETHERING_USB); - mLooper.dispatchAll(); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - mLooper.dispatchAll(); - } - - private void initOffloadConfiguration(final boolean offloadConfig, - final boolean offloadControl, final int defaultDisabled) { - when(mOffloadHardwareInterface.initOffloadConfig()).thenReturn(offloadConfig); - when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControl); - when(mOffloadHardwareInterface.getDefaultTetherOffloadDisabled()).thenReturn( - defaultDisabled); - } - - @Test - public void testMultiSimAware() throws Exception { - final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration(); - assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.activeDataSubId); - - final int fakeSubId = 1234; - mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); - final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); - assertEquals(fakeSubId, newConfig.activeDataSubId); - verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId)); - } - - @Test - public void testNoDuplicatedEthernetRequest() throws Exception { - final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); - when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); - mLooper.dispatchAll(); - verify(mEm, times(1)).requestTetheredInterface(any(), any()); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mEm); - mTethering.stopTethering(TETHERING_ETHERNET); - mLooper.dispatchAll(); - verify(mockRequest, times(1)).release(); - mTethering.stopTethering(TETHERING_ETHERNET); - mLooper.dispatchAll(); - verifyNoMoreInteractions(mEm); - } - - private void workingWifiP2pGroupOwner( - boolean emulateInterfaceStatusChanged) throws Exception { - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); - } - sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verifyInterfaceServingModeStarted(TEST_P2P_IFNAME); - verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, times(1)).tetherStartWithConfiguration(any()); - verifyNoMoreInteractions(mNetd); - verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); - verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); - // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY - verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE); - - assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - - // Emulate externally-visible WifiP2pManager effects, when wifi p2p group - // is being removed. - sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME); - mTethering.interfaceRemoved(TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, times(1)).tetherApplyDnsInterfaces(); - verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME); - verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - // interfaceSetCfg() called once for enabling and twice for disabling IPv4. - verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, times(1)).tetherStop(); - verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); - verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream(); - verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); - verifyNoMoreInteractions(mNetd); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - } - - private void workingWifiP2pGroupClient( - boolean emulateInterfaceStatusChanged) throws Exception { - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); - } - sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); - verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, never()).tetherStartWithConfiguration(any()); - - // Emulate externally-visible WifiP2pManager effects, when wifi p2p group - // is being removed. - sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME); - mTethering.interfaceRemoved(TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, never()).tetherApplyDnsInterfaces(); - verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME); - verify(mNetd, never()).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, never()).tetherStop(); - verify(mNetd, never()).ipfwdDisableForwarding(TETHERING_NAME); - verifyNoMoreInteractions(mNetd); - // Asking for the last error after the per-interface state machine - // has been reaped yields an unknown interface error. - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - } - - @Test - public void workingWifiP2pGroupOwnerWithIfaceChanged() throws Exception { - workingWifiP2pGroupOwner(true); - } - - @Test - public void workingWifiP2pGroupOwnerSansIfaceChanged() throws Exception { - workingWifiP2pGroupOwner(false); - } - - private void workingWifiP2pGroupOwnerLegacyMode( - boolean emulateInterfaceStatusChanged) throws Exception { - // change to legacy mode and update tethering information by chaning SIM - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{}); - final int fakeSubId = 1234; - mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); - - if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); - } - sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); - - verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); - verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); - verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); - verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); - verify(mNetd, never()).tetherStartWithConfiguration(any()); - assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); - } - @Test - public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception { - workingWifiP2pGroupOwnerLegacyMode(true); - } - - @Test - public void workingWifiP2pGroupOwnerLegacyModeSansIfaceChanged() throws Exception { - workingWifiP2pGroupOwnerLegacyMode(false); - } - - @Test - public void workingWifiP2pGroupClientWithIfaceChanged() throws Exception { - workingWifiP2pGroupClient(true); - } - - @Test - public void workingWifiP2pGroupClientSansIfaceChanged() throws Exception { - workingWifiP2pGroupClient(false); - } - - private void setDataSaverEnabled(boolean enabled) { - final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED); - mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL); - - final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED - : RESTRICT_BACKGROUND_STATUS_DISABLED; - when(mCm.getRestrictBackgroundStatus()).thenReturn(status); - mLooper.dispatchAll(); - } - - @Test - public void testDataSaverChanged() { - // Start Tethering. - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - runUsbTethering(upstreamState); - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - // Data saver is ON. - setDataSaverEnabled(true); - // Verify that tethering should be disabled. - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - mLooper.dispatchAll(); - assertEquals(mTethering.getTetheredIfaces(), new String[0]); - reset(mUsbManager); - - runUsbTethering(upstreamState); - // Verify that user can start tethering again without turning OFF data saver. - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - - // If data saver is keep ON with change event, tethering should not be OFF this time. - setDataSaverEnabled(true); - verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - - // If data saver is turned OFF, it should not change tethering. - setDataSaverEnabled(false); - verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); - } - - private static <T> void assertContains(Collection<T> collection, T element) { - assertTrue(element + " not found in " + collection, collection.contains(element)); - } - - private class ResultListener extends IIntResultListener.Stub { - private final int mExpectedResult; - private boolean mHasResult = false; - ResultListener(final int expectedResult) { - mExpectedResult = expectedResult; - } - - @Override - public void onResult(final int resultCode) { - mHasResult = true; - if (resultCode != mExpectedResult) { - fail("expected result: " + mExpectedResult + " but actual result: " + resultCode); - } - } - - public void assertHasResult() { - if (!mHasResult) fail("No callback result"); - } - } - - @Test - public void testMultipleStartTethering() throws Exception { - final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24"); - final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24"); - final String serverAddr = "192.168.20.1"; - final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR); - final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR); - final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR); - - // Enable USB tethering and check that Tethering starts USB. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - null, null, false), firstResult); - mLooper.dispatchAll(); - firstResult.assertHasResult(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - verifyNoMoreInteractions(mUsbManager); - - // Enable USB tethering again with the same request and expect no change to USB. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - null, null, false), secondResult); - mLooper.dispatchAll(); - secondResult.assertHasResult(); - verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); - reset(mUsbManager); - - // Enable USB tethering with a different request and expect that USB is stopped and - // started. - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false), thirdResult); - mLooper.dispatchAll(); - thirdResult.assertHasResult(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - - // Expect that when USB comes up, the DHCP server is configured with the requested address. - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); - } - - @Test - public void testRequestStaticIp() throws Exception { - final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24"); - final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24"); - final String serverAddr = "192.168.0.123"; - final int clientAddrParceled = 0xc0a8002a; - final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor = - ArgumentCaptor.forClass(DhcpServingParamsParcel.class); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false), null); - mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); - verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(), - any()); - final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue(); - assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress()); - assertEquals(24, params.serverAddrPrefixLength); - assertEquals(clientAddrParceled, params.singleClientAddr); - } - - @Test - public void testUpstreamNetworkChanged() { - final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM) - mTetheringDependencies.mUpstreamNetworkMonitorSM; - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - initTetheringUpstream(upstreamState); - stateMachine.chooseUpstreamType(true); - - verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); - verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any()); - } - - @Test - public void testUpstreamCapabilitiesChanged() { - final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM) - mTetheringDependencies.mUpstreamNetworkMonitorSM; - final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - initTetheringUpstream(upstreamState); - stateMachine.chooseUpstreamType(true); - - stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState); - // Should have two onUpstreamCapabilitiesChanged(). - // One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES. - verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any()); - reset(mNotificationUpdater); - - // Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network - // capabilities changed. - final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState( - upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101)); - stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2); - verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any()); - } - - @Test - public void testDumpTetheringLog() throws Exception { - final FileDescriptor mockFd = mock(FileDescriptor.class); - final PrintWriter mockPw = mock(PrintWriter.class); - runUsbTethering(null); - mLooper.startAutoDispatch(); - mTethering.dump(mockFd, mockPw, new String[0]); - verify(mConfig).dump(any()); - verify(mEntitleMgr).dump(any()); - verify(mOffloadCtrl).dump(any()); - mLooper.stopAutoDispatch(); - } - - @Test - public void testExemptFromEntitlementCheck() throws Exception { - setupForRequiredProvisioning(); - final TetheringRequestParcel wifiNotExemptRequest = - createTetheringRequestParcel(TETHERING_WIFI, null, null, false); - mTethering.startTethering(wifiNotExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); - assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); - reset(mEntitleMgr); - - setupForRequiredProvisioning(); - final TetheringRequestParcel wifiExemptRequest = - createTetheringRequestParcel(TETHERING_WIFI, null, null, true); - mTethering.startTethering(wifiExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); - assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); - reset(mEntitleMgr); - - // If one app enables tethering without provisioning check first, then another app enables - // tethering of the same type but does not disable the provisioning check. - setupForRequiredProvisioning(); - mTethering.startTethering(wifiExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); - assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); - reset(mEntitleMgr); - setupForRequiredProvisioning(); - mTethering.startTethering(wifiNotExemptRequest, null); - mLooper.dispatchAll(); - verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); - verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); - assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); - mTethering.stopTethering(TETHERING_WIFI); - mLooper.dispatchAll(); - verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); - reset(mEntitleMgr); - } - - private void setupForRequiredProvisioning() { - // Produce some acceptable looking provision app setting if requested. - when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) - .thenReturn(PROVISIONING_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - // Act like the CarrierConfigManager is present and ready unless told otherwise. - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(mCarrierConfigManager); - when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); - sendConfigurationChanged(); - } - - private static UpstreamNetworkState buildV4UpstreamState(final LinkAddress address, - final Network network, final String iface, final int transportType) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(iface); - - prop.addLinkAddress(address); - - final NetworkCapabilities capabilities = new NetworkCapabilities() - .addTransportType(transportType); - return new UpstreamNetworkState(prop, capabilities, network); - } - - private void updateV4Upstream(final LinkAddress ipv4Address, final Network network, - final String iface, final int transportType) { - final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface, - transportType); - mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage( - Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, - 0, - upstream); - mLooper.dispatchAll(); - } - - @Test - public void testHandleIpConflict() throws Exception { - final Network wifiNetwork = new Network(200); - final Network[] allNetworks = { wifiNetwork }; - when(mCm.getAllNetworks()).thenReturn(allNetworks); - runUsbTethering(null); - final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor = - ArgumentCaptor.forClass(InterfaceConfigurationParcel.class); - verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture()); - final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr; - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - reset(mNetd, mUsbManager); - - // Cause a prefix conflict by assigning a /30 out of the downstream's /24 to the upstream. - updateV4Upstream(new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), 30), - wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI); - // verify turn off usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - mLooper.dispatchAll(); - // verify restart usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - } - - @Test - public void testNoAddressAvailable() throws Exception { - final Network wifiNetwork = new Network(200); - final Network btNetwork = new Network(201); - final Network mobileNetwork = new Network(202); - final Network[] allNetworks = { wifiNetwork, btNetwork, mobileNetwork }; - when(mCm.getAllNetworks()).thenReturn(allNetworks); - runUsbTethering(null); - verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( - any(), any()); - reset(mUsbManager); - final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); - when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); - final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor = - ArgumentCaptor.forClass(TetheredInterfaceCallback.class); - mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); - mLooper.dispatchAll(); - verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture()); - TetheredInterfaceCallback ethCallback = callbackCaptor.getValue(); - ethCallback.onAvailable(TEST_ETH_IFNAME); - mLooper.dispatchAll(); - reset(mUsbManager, mEm); - - updateV4Upstream(new LinkAddress("192.168.0.100/16"), wifiNetwork, TEST_WIFI_IFNAME, - TRANSPORT_WIFI); - updateV4Upstream(new LinkAddress("172.16.0.0/12"), btNetwork, TEST_BT_IFNAME, - TRANSPORT_BLUETOOTH); - updateV4Upstream(new LinkAddress("10.0.0.0/8"), mobileNetwork, TEST_MOBILE_IFNAME, - TRANSPORT_CELLULAR); - - mLooper.dispatchAll(); - // verify turn off usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); - // verify turn off ethernet tethering - verify(mockRequest).release(); - mTethering.interfaceRemoved(TEST_USB_IFNAME); - ethCallback.onUnavailable(); - mLooper.dispatchAll(); - // verify restart usb tethering - verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - // verify restart ethernet tethering - verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture()); - ethCallback = callbackCaptor.getValue(); - ethCallback.onAvailable(TEST_ETH_IFNAME); - - reset(mUsbManager, mEm); - when(mNetd.interfaceGetList()) - .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, - TEST_NCM_IFNAME, TEST_ETH_IFNAME}); - - mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); - sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); - assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME); - assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME); - assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME)); - assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME)); - } - - @Test - public void testProvisioningNeededButUnavailable() throws Exception { - assertTrue(mTethering.isTetheringSupported()); - verify(mPackageManager, never()).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - - setupForRequiredProvisioning(); - assertTrue(mTethering.isTetheringSupported()); - verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - reset(mPackageManager); - - doThrow(PackageManager.NameNotFoundException.class).when(mPackageManager).getPackageInfo( - PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - setupForRequiredProvisioning(); - assertFalse(mTethering.isTetheringSupported()); - verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); - } - - // TODO: Test that a request for hotspot mode doesn't interfere with an - // already operating tethering mode interface. -} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java deleted file mode 100644 index 232588c7eec0..000000000000 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.tethering; - -import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; -import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; - -import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.IConnectivityManager; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.Message; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class UpstreamNetworkMonitorTest { - private static final int EVENT_UNM_UPDATE = 1; - - private static final boolean INCLUDES = true; - private static final boolean EXCLUDES = false; - - // Actual contents of the request don't matter for this test. The lack of - // any specific TRANSPORT_* is sufficient to identify this request. - private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build(); - - @Mock private Context mContext; - @Mock private EntitlementManager mEntitleMgr; - @Mock private IConnectivityManager mCS; - @Mock private SharedLog mLog; - - private TestStateMachine mSM; - private TestConnectivityManager mCM; - private UpstreamNetworkMonitor mUNM; - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - reset(mContext); - reset(mCS); - reset(mLog); - when(mLog.forSubComponent(anyString())).thenReturn(mLog); - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - - mCM = spy(new TestConnectivityManager(mContext, mCS)); - mSM = new TestStateMachine(); - mUNM = new UpstreamNetworkMonitor( - (ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE); - } - - @After public void tearDown() throws Exception { - if (mSM != null) { - mSM.quit(); - mSM = null; - } - } - - @Test - public void testStopWithoutStartIsNonFatal() { - mUNM.stop(); - mUNM.stop(); - mUNM.stop(); - } - - @Test - public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception { - assertTrue(mCM.hasNoCallbacks()); - assertFalse(mUNM.mobileNetworkRequested()); - - mUNM.updateMobileRequiresDun(true); - assertTrue(mCM.hasNoCallbacks()); - mUNM.updateMobileRequiresDun(false); - assertTrue(mCM.hasNoCallbacks()); - } - - @Test - public void testDefaultNetworkIsTracked() throws Exception { - assertTrue(mCM.hasNoCallbacks()); - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - - mUNM.startObserveAllNetworks(); - assertEquals(1, mCM.trackingDefault.size()); - - mUNM.stop(); - assertTrue(mCM.onlyHasDefaultCallbacks()); - } - - @Test - public void testListensForAllNetworks() throws Exception { - assertTrue(mCM.listening.isEmpty()); - - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - assertFalse(mCM.listening.isEmpty()); - assertTrue(mCM.isListeningForAll()); - - mUNM.stop(); - assertTrue(mCM.onlyHasDefaultCallbacks()); - } - - @Test - public void testCallbacksRegistered() { - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - verify(mCM, times(1)).requestNetwork( - eq(sDefaultRequest), any(NetworkCallback.class), any(Handler.class)); - mUNM.startObserveAllNetworks(); - verify(mCM, times(1)).registerNetworkCallback( - any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); - - mUNM.stop(); - verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class)); - } - - @Test - public void testRequestsMobileNetwork() throws Exception { - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.startObserveAllNetworks(); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.updateMobileRequiresDun(false); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.registerMobileNetworkRequest(); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(isDunRequested()); - - mUNM.stop(); - assertFalse(mUNM.mobileNetworkRequested()); - assertTrue(mCM.hasNoCallbacks()); - } - - @Test - public void testDuplicateMobileRequestsIgnored() throws Exception { - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.startObserveAllNetworks(); - verify(mCM, times(1)).registerNetworkCallback( - any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.updateMobileRequiresDun(true); - mUNM.registerMobileNetworkRequest(); - verify(mCM, times(1)).requestNetwork( - any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class), - any(NetworkCallback.class)); - - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - // Try a few things that must not result in any state change. - mUNM.registerMobileNetworkRequest(); - mUNM.updateMobileRequiresDun(true); - mUNM.registerMobileNetworkRequest(); - - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - mUNM.stop(); - verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class)); - - verifyNoMoreInteractions(mCM); - } - - @Test - public void testRequestsDunNetwork() throws Exception { - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.startObserveAllNetworks(); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.updateMobileRequiresDun(true); - assertFalse(mUNM.mobileNetworkRequested()); - assertEquals(0, mCM.requested.size()); - - mUNM.registerMobileNetworkRequest(); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - mUNM.stop(); - assertFalse(mUNM.mobileNetworkRequested()); - assertTrue(mCM.hasNoCallbacks()); - } - - @Test - public void testUpdateMobileRequiresDun() throws Exception { - mUNM.startObserveAllNetworks(); - - // Test going from no-DUN to DUN correctly re-registers callbacks. - mUNM.updateMobileRequiresDun(false); - mUNM.registerMobileNetworkRequest(); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(isDunRequested()); - mUNM.updateMobileRequiresDun(true); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(isDunRequested()); - - // Test going from DUN to no-DUN correctly re-registers callbacks. - mUNM.updateMobileRequiresDun(false); - assertTrue(mUNM.mobileNetworkRequested()); - assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(isDunRequested()); - - mUNM.stop(); - assertFalse(mUNM.mobileNetworkRequested()); - } - - @Test - public void testSelectPreferredUpstreamType() throws Exception { - final Collection<Integer> preferredTypes = new ArrayList<>(); - preferredTypes.add(TYPE_WIFI); - - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - // There are no networks, so there is nothing to select. - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - wifiAgent.fakeConnect(); - // WiFi is up, we should prefer it. - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - wifiAgent.fakeDisconnect(); - // There are no networks, so there is nothing to select. - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - cellAgent.fakeConnect(); - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - - preferredTypes.add(TYPE_MOBILE_DUN); - // This is coupled with preferred types in TetheringConfiguration. - mUNM.updateMobileRequiresDun(true); - // DUN is available, but only use regular cell: no upstream selected. - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - preferredTypes.remove(TYPE_MOBILE_DUN); - // No WiFi, but our preferred flavour of cell is up. - preferredTypes.add(TYPE_MOBILE_HIPRI); - // This is coupled with preferred types in TetheringConfiguration. - mUNM.updateMobileRequiresDun(false); - assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, - mUNM.selectPreferredUpstreamType(preferredTypes)); - // Check to see we filed an explicit request. - assertEquals(1, mCM.requested.size()); - NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0]; - assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)); - // mobile is not permitted, we should not use HIPRI. - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - assertEquals(0, mCM.requested.size()); - // mobile change back to permitted, HIRPI should come back - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, - mUNM.selectPreferredUpstreamType(preferredTypes)); - - wifiAgent.fakeConnect(); - // WiFi is up, and we should prefer it over cell. - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - assertEquals(0, mCM.requested.size()); - - preferredTypes.remove(TYPE_MOBILE_HIPRI); - preferredTypes.add(TYPE_MOBILE_DUN); - // This is coupled with preferred types in TetheringConfiguration. - mUNM.updateMobileRequiresDun(true); - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - - final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); - dunAgent.fakeConnect(); - - // WiFi is still preferred. - assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); - - // WiFi goes down, cell and DUN are still up but only DUN is preferred. - wifiAgent.fakeDisconnect(); - assertSatisfiesLegacyType(TYPE_MOBILE_DUN, - mUNM.selectPreferredUpstreamType(preferredTypes)); - // Check to see we filed an explicit request. - assertEquals(1, mCM.requested.size()); - netReq = (NetworkRequest) mCM.requested.values().toArray()[0]; - assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)); - // mobile is not permitted, we should not use DUN. - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); - assertEquals(0, mCM.requested.size()); - // mobile change back to permitted, DUN should come back - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - assertSatisfiesLegacyType(TYPE_MOBILE_DUN, - mUNM.selectPreferredUpstreamType(preferredTypes)); - } - - @Test - public void testGetCurrentPreferredUpstream() throws Exception { - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - mUNM.updateMobileRequiresDun(false); - - // [0] Mobile connects, DUN not required -> mobile selected. - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - cellAgent.fakeConnect(); - mCM.makeDefaultNetwork(cellAgent); - assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [1] Mobile connects but not permitted -> null selected - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertEquals(null, mUNM.getCurrentPreferredUpstream()); - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); - - // [2] WiFi connects but not validated/promoted to default -> mobile selected. - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - wifiAgent.fakeConnect(); - assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [3] WiFi validates and is promoted to the default network -> WiFi selected. - mCM.makeDefaultNetwork(wifiAgent); - assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [4] DUN required, no other changes -> WiFi still selected - mUNM.updateMobileRequiresDun(true); - assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected. - mCM.makeDefaultNetwork(cellAgent); - assertEquals(null, mUNM.getCurrentPreferredUpstream()); - // TODO: make sure that a DUN request has been filed. This is currently - // triggered by code over in Tethering, but once that has been moved - // into UNM we should test for this here. - - // [6] DUN network arrives -> DUN selected - final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); - dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); - dunAgent.fakeConnect(); - assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network); - - // [7] Mobile is not permitted -> null selected - when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false); - assertEquals(null, mUNM.getCurrentPreferredUpstream()); - } - - @Test - public void testLocalPrefixes() throws Exception { - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - - // [0] Test minimum set of local prefixes. - Set<IpPrefix> local = mUNM.getLocalPrefixes(); - assertTrue(local.isEmpty()); - - final Set<String> alreadySeen = new HashSet<>(); - - // [1] Pretend Wi-Fi connects. - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - final LinkProperties wifiLp = wifiAgent.linkProperties; - wifiLp.setInterfaceName("wlan0"); - final String[] wifi_addrs = { - "fe80::827a:bfff:fe6f:374d", "100.112.103.18", - "2001:db8:4:fd00:827a:bfff:fe6f:374d", - "2001:db8:4:fd00:6dea:325a:fdae:4ef4", - "fd6a:a640:60bf:e985::123", // ULA address for good measure. - }; - for (String addrStr : wifi_addrs) { - final String cidr = addrStr.contains(":") ? "/64" : "/20"; - wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr)); - } - wifiAgent.fakeConnect(); - wifiAgent.sendLinkProperties(); - - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, INCLUDES, alreadySeen); - final String[] wifiLinkPrefixes = { - // Link-local prefixes are excluded and dealt with elsewhere. - "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64", - }; - assertPrefixSet(local, INCLUDES, wifiLinkPrefixes); - Collections.addAll(alreadySeen, wifiLinkPrefixes); - assertEquals(alreadySeen.size(), local.size()); - - // [2] Pretend mobile connects. - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - final LinkProperties cellLp = cellAgent.linkProperties; - cellLp.setInterfaceName("rmnet_data0"); - final String[] cell_addrs = { - "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d", - }; - for (String addrStr : cell_addrs) { - final String cidr = addrStr.contains(":") ? "/64" : "/27"; - cellLp.addLinkAddress(new LinkAddress(addrStr + cidr)); - } - cellAgent.fakeConnect(); - cellAgent.sendLinkProperties(); - - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, INCLUDES, alreadySeen); - final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" }; - assertPrefixSet(local, INCLUDES, cellLinkPrefixes); - Collections.addAll(alreadySeen, cellLinkPrefixes); - assertEquals(alreadySeen.size(), local.size()); - - // [3] Pretend DUN connects. - final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); - dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); - final LinkProperties dunLp = dunAgent.linkProperties; - dunLp.setInterfaceName("rmnet_data1"); - final String[] dun_addrs = { - "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d", - }; - for (String addrStr : dun_addrs) { - final String cidr = addrStr.contains(":") ? "/64" : "/27"; - dunLp.addLinkAddress(new LinkAddress(addrStr + cidr)); - } - dunAgent.fakeConnect(); - dunAgent.sendLinkProperties(); - - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, INCLUDES, alreadySeen); - final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" }; - assertPrefixSet(local, INCLUDES, dunLinkPrefixes); - Collections.addAll(alreadySeen, dunLinkPrefixes); - assertEquals(alreadySeen.size(), local.size()); - - // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no - // longer be included (should be properly removed). - wifiAgent.fakeDisconnect(); - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(local, INCLUDES, cellLinkPrefixes); - assertPrefixSet(local, INCLUDES, dunLinkPrefixes); - - // [5] Pretend mobile disconnected. - cellAgent.fakeDisconnect(); - local = mUNM.getLocalPrefixes(); - assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(local, EXCLUDES, cellLinkPrefixes); - assertPrefixSet(local, INCLUDES, dunLinkPrefixes); - - // [6] Pretend DUN disconnected. - dunAgent.fakeDisconnect(); - local = mUNM.getLocalPrefixes(); - assertTrue(local.isEmpty()); - } - - @Test - public void testSelectMobileWhenMobileIsNotDefault() { - final Collection<Integer> preferredTypes = new ArrayList<>(); - // Mobile has higher pirority than wifi. - preferredTypes.add(TYPE_MOBILE_HIPRI); - preferredTypes.add(TYPE_WIFI); - mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); - mUNM.startObserveAllNetworks(); - // Setup wifi and make wifi as default network. - final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); - wifiAgent.fakeConnect(); - mCM.makeDefaultNetwork(wifiAgent); - // Setup mobile network. - final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); - cellAgent.fakeConnect(); - - assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, - mUNM.selectPreferredUpstreamType(preferredTypes)); - verify(mEntitleMgr, times(1)).maybeRunProvisioning(); - } - - private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) { - if (legacyType == TYPE_NONE) { - assertTrue(ns == null); - return; - } - - final NetworkCapabilities nc = - UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType); - assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities)); - } - - private void assertUpstreamTypeRequested(int upstreamType) throws Exception { - assertEquals(1, mCM.requested.size()); - assertEquals(1, mCM.legacyTypeMap.size()); - assertEquals(Integer.valueOf(upstreamType), - mCM.legacyTypeMap.values().iterator().next()); - } - - private boolean isDunRequested() { - for (NetworkRequest req : mCM.requested.values()) { - if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { - return true; - } - } - return false; - } - - public static class TestConnectivityManager extends ConnectivityManager { - public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>(); - public Set<NetworkCallback> trackingDefault = new HashSet<>(); - public TestNetworkAgent defaultNetwork = null; - public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>(); - public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>(); - public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>(); - - private int mNetworkId = 100; - - public TestConnectivityManager(Context ctx, IConnectivityManager svc) { - super(ctx, svc); - } - - boolean hasNoCallbacks() { - return allCallbacks.isEmpty() - && trackingDefault.isEmpty() - && listening.isEmpty() - && requested.isEmpty() - && legacyTypeMap.isEmpty(); - } - - boolean onlyHasDefaultCallbacks() { - return (allCallbacks.size() == 1) - && (trackingDefault.size() == 1) - && listening.isEmpty() - && requested.isEmpty() - && legacyTypeMap.isEmpty(); - } - - boolean isListeningForAll() { - final NetworkCapabilities empty = new NetworkCapabilities(); - empty.clearAll(); - - for (NetworkRequest req : listening.values()) { - if (req.networkCapabilities.equalRequestableCapabilities(empty)) { - return true; - } - } - return false; - } - - int getNetworkId() { - return ++mNetworkId; - } - - void makeDefaultNetwork(TestNetworkAgent agent) { - if (Objects.equals(defaultNetwork, agent)) return; - - final TestNetworkAgent formerDefault = defaultNetwork; - defaultNetwork = agent; - - for (NetworkCallback cb : trackingDefault) { - if (defaultNetwork != null) { - cb.onAvailable(defaultNetwork.networkId); - cb.onCapabilitiesChanged( - defaultNetwork.networkId, defaultNetwork.networkCapabilities); - cb.onLinkPropertiesChanged( - defaultNetwork.networkId, defaultNetwork.linkProperties); - } - } - } - - @Override - public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { - assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); - if (sDefaultRequest.equals(req)) { - assertFalse(trackingDefault.contains(cb)); - trackingDefault.add(cb); - } else { - assertFalse(requested.containsKey(cb)); - requested.put(cb, req); - } - } - - @Override - public void requestNetwork(NetworkRequest req, NetworkCallback cb) { - fail("Should never be called."); - } - - @Override - public void requestNetwork(NetworkRequest req, - int timeoutMs, int legacyType, Handler h, NetworkCallback cb) { - assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); - assertFalse(requested.containsKey(cb)); - requested.put(cb, req); - assertFalse(legacyTypeMap.containsKey(cb)); - if (legacyType != ConnectivityManager.TYPE_NONE) { - legacyTypeMap.put(cb, legacyType); - } - } - - @Override - public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) { - assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); - assertFalse(listening.containsKey(cb)); - listening.put(cb, req); - } - - @Override - public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) { - fail("Should never be called."); - } - - @Override - public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) { - fail("Should never be called."); - } - - @Override - public void registerDefaultNetworkCallback(NetworkCallback cb) { - fail("Should never be called."); - } - - @Override - public void unregisterNetworkCallback(NetworkCallback cb) { - if (trackingDefault.contains(cb)) { - trackingDefault.remove(cb); - } else if (listening.containsKey(cb)) { - listening.remove(cb); - } else if (requested.containsKey(cb)) { - requested.remove(cb); - legacyTypeMap.remove(cb); - } else { - fail("Unexpected callback removed"); - } - allCallbacks.remove(cb); - - assertFalse(allCallbacks.containsKey(cb)); - assertFalse(trackingDefault.contains(cb)); - assertFalse(listening.containsKey(cb)); - assertFalse(requested.containsKey(cb)); - } - } - - public static class TestNetworkAgent { - public final TestConnectivityManager cm; - public final Network networkId; - public final int transportType; - public final NetworkCapabilities networkCapabilities; - public final LinkProperties linkProperties; - - public TestNetworkAgent(TestConnectivityManager cm, int transportType) { - this.cm = cm; - this.networkId = new Network(cm.getNetworkId()); - this.transportType = transportType; - networkCapabilities = new NetworkCapabilities(); - networkCapabilities.addTransportType(transportType); - networkCapabilities.addCapability(NET_CAPABILITY_INTERNET); - linkProperties = new LinkProperties(); - } - - public void fakeConnect() { - for (NetworkCallback cb : cm.listening.keySet()) { - cb.onAvailable(networkId); - cb.onCapabilitiesChanged(networkId, copy(networkCapabilities)); - cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); - } - } - - public void fakeDisconnect() { - for (NetworkCallback cb : cm.listening.keySet()) { - cb.onLost(networkId); - } - } - - public void sendLinkProperties() { - for (NetworkCallback cb : cm.listening.keySet()) { - cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); - } - } - - @Override - public String toString() { - return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities); - } - } - - public static class TestStateMachine extends StateMachine { - public final ArrayList<Message> messages = new ArrayList<>(); - private final State mLoggingState = new LoggingState(); - - class LoggingState extends State { - @Override public void enter() { - messages.clear(); - } - - @Override public void exit() { - messages.clear(); - } - - @Override public boolean processMessage(Message msg) { - messages.add(msg); - return true; - } - } - - public TestStateMachine() { - super("UpstreamNetworkMonitor.TestStateMachine"); - addState(mLoggingState); - setInitialState(mLoggingState); - super.start(); - } - } - - static NetworkCapabilities copy(NetworkCapabilities nc) { - return new NetworkCapabilities(nc); - } - - static LinkProperties copy(LinkProperties lp) { - return new LinkProperties(lp); - } - - static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) { - final Set<String> expectedSet = new HashSet<>(); - Collections.addAll(expectedSet, expected); - assertPrefixSet(prefixes, expectation, expectedSet); - } - - static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) { - for (String expectedPrefix : expected) { - final String errStr = expectation ? "did not find" : "found"; - assertEquals( - String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix), - expectation, prefixes.contains(new IpPrefix(expectedPrefix))); - } - } -} diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 92b8608f4f6c..bd26d44bed6f 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -25,6 +25,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; +import android.content.ClipData; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -296,11 +297,29 @@ final class RemoteAugmentedAutofillService dataset.getId(), clientState); try { final ArrayList<AutofillId> fieldIds = dataset.getFieldIds(); - final int size = fieldIds.size(); - final boolean hideHighlight = size == 1 - && fieldIds.get(0).equals(focusedId); - client.autofill(sessionId, fieldIds, dataset.getFieldValues(), - hideHighlight); + final ClipData content = dataset.getFieldContent(); + if (content != null) { + final AutofillId fieldId = fieldIds.get(0); + if (sDebug) { + Slog.d(TAG, "Calling client autofillContent(): " + + "id=" + fieldId + ", content=" + content); + } + client.autofillContent(sessionId, fieldId, content); + } else { + final int size = fieldIds.size(); + final boolean hideHighlight = size == 1 + && fieldIds.get(0).equals(focusedId); + if (sDebug) { + Slog.d(TAG, "Calling client autofill(): " + + "ids=" + fieldIds + + ", values=" + dataset.getFieldValues()); + } + client.autofill( + sessionId, + fieldIds, + dataset.getFieldValues(), + hideHighlight); + } inlineSuggestionsCallback.apply( InlineFillUi.emptyUi(focusedId)); } catch (RemoteException e) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index f596b072d713..0302b2251f10 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -47,6 +47,7 @@ import android.app.IAssistDataReceiver; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.AutofillOverlay; import android.app.assist.AssistStructure.ViewNode; +import android.content.ClipData; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -1493,11 +1494,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id + ", authId=" + authId + ", dataset=" + dataset); } - if (dataset == null - || dataset.getFieldIds().size() != 1 - || dataset.getFieldIds().get(0) == null - || dataset.getFieldValues().size() != 1 - || dataset.getFieldValues().get(0) == null) { + final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1) + ? dataset.getFieldIds().get(0) : null; + final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1) + ? dataset.getFieldValues().get(0) : null; + final ClipData content = (dataset != null) ? dataset.getFieldContent() : null; + if (fieldId == null || (value == null && content == null)) { if (sDebug) { Slog.d(TAG, "Rejecting empty/invalid auth result"); } @@ -1505,10 +1507,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeSelfLocked(); return; } - final List<AutofillId> fieldIds = dataset.getFieldIds(); - final List<AutofillValue> autofillValues = dataset.getFieldValues(); - final AutofillId fieldId = fieldIds.get(0); - final AutofillValue value = autofillValues.get(0); // Update state to ensure that after filling the field here we don't end up firing another // autofill request that will end up showing the same suggestions to the user again. When @@ -1524,13 +1522,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Fill the value into the field. if (sDebug) { - Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value); + Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value + + ", content=" + content); } try { - mClient.autofill(id, fieldIds, autofillValues, true); + if (content != null) { + mClient.autofillContent(id, fieldId, content); + } else { + mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true); + } } catch (RemoteException e) { Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value - + ", error=" + e); + + ", content=" + content, e); } // Clear the suggestions since the user already accepted one of them. diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 032820dc97f8..e32324941aef 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -309,6 +309,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mFindDeviceCallback = callback; mRequest = request; mCallingPackage = callingPackage; + request.setCallingPackage(callingPackage); callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0); final long callingIdentity = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 85d77f246bbb..21ad6de045bb 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2431,9 +2431,9 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu); - mNMS.setMtu(iface, mtu); - } catch (Exception e) { - Slog.e(TAG, "exception in setMtu()" + e); + mNetd.interfaceSetMtu(iface, mtu); + } catch (RemoteException | ServiceSpecificException e) { + Slog.e(TAG, "exception in interfaceSetMtu()" + e); } } @@ -6078,7 +6078,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (final String iface : interfaceDiff.added) { try { if (DBG) log("Adding iface " + iface + " to network " + netId); - mNMS.addInterfaceToNetwork(iface, netId); + mNetd.networkAddInterface(netId, iface); wakeupModifyInterface(iface, caps, true); bs.noteNetworkInterfaceType(iface, legacyType); } catch (Exception e) { @@ -6090,7 +6090,7 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (DBG) log("Removing iface " + iface + " from network " + netId); wakeupModifyInterface(iface, caps, false); - mNMS.removeInterfaceFromNetwork(iface, netId); + mNetd.networkRemoveInterface(netId, iface); } catch (Exception e) { loge("Exception removing interface: " + e); } @@ -6256,9 +6256,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final int newPermission = getNetworkPermission(newNc); if (oldPermission != newPermission && nai.created && !nai.isVPN()) { try { - mNMS.setNetworkPermission(nai.network.netId, newPermission); - } catch (RemoteException e) { - loge("Exception in setNetworkPermission: " + e); + mNetd.networkSetPermissionForNetwork(nai.network.netId, newPermission); + } catch (RemoteException | ServiceSpecificException e) { + loge("Exception in networkSetPermissionForNetwork: " + e); } } } @@ -6700,11 +6700,11 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (null != newNetwork) { - mNMS.setDefaultNetId(newNetwork.network.netId); + mNetd.networkSetDefault(newNetwork.network.netId); } else { - mNMS.clearDefaultNetId(); + mNetd.networkClearDefault(); } - } catch (Exception e) { + } catch (RemoteException | ServiceSpecificException e) { loge("Exception setting default network :" + e); } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index ea14fadff433..1c99465dfebf 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -945,17 +945,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } @Override - public void setMtu(String iface, int mtu) { - NetworkStack.checkNetworkStackPermission(mContext); - - try { - mNetdService.interfaceSetMtu(iface, mtu); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override public void shutdown() { // TODO: remove from aidl if nobody calls externally mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG); @@ -1985,16 +1974,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { pw.println("]"); } - @Override - public void addInterfaceToNetwork(String iface, int netId) { - modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, netId, iface); - } - - @Override - public void removeInterfaceFromNetwork(String iface, int netId) { - modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, netId, iface); - } - private void modifyInterfaceInNetwork(boolean add, int netId, String iface) { NetworkStack.checkNetworkStackPermission(mContext); try { @@ -2030,39 +2009,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } @Override - public void setDefaultNetId(int netId) { - NetworkStack.checkNetworkStackPermission(mContext); - - try { - mNetdService.networkSetDefault(netId); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override - public void clearDefaultNetId() { - NetworkStack.checkNetworkStackPermission(mContext); - - try { - mNetdService.networkClearDefault(); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override - public void setNetworkPermission(int netId, int permission) { - NetworkStack.checkNetworkStackPermission(mContext); - - try { - mNetdService.networkSetPermissionForNetwork(netId, permission); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - } - - @Override public void allowProtect(int uid) { NetworkStack.checkNetworkStackPermission(mContext); diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 0135b9d2f8ab..7051452f25cc 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -501,7 +501,7 @@ final class UiModeManagerService extends SystemService { } /** - * Updates the night mode setting in Settings.Global and returns if the value was successfully + * Updates the night mode setting in Settings.Secure and returns if the value was successfully * changed. * * @param context A valid context @@ -516,7 +516,8 @@ final class UiModeManagerService extends SystemService { int oldNightMode = mNightMode; if (mSetupWizardComplete) { mNightMode = Secure.getIntForUser(context.getContentResolver(), - Secure.UI_NIGHT_MODE, mNightMode, userId); + Secure.UI_NIGHT_MODE, res.getInteger( + com.android.internal.R.integer.config_defaultNightMode), userId); mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java new file mode 100644 index 000000000000..db7e16ca8b25 --- /dev/null +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.vcn.IVcnManagementService; + +/** + * VcnManagementService manages Virtual Carrier Network profiles and lifecycles. + * + * <pre>The internal structure of the VCN Management subsystem is as follows: + * + * +------------------------+ 1:1 +--------------------------------+ + * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager | + * | | | | + * | Manages configs and | | Tracks subscriptions, carrier | + * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps | + * +------------------------+ carrier privilege changes +--------------------------------+ + * | 1:N ^ + * | | + * | +-------------------------------+ + * +---------------+ | + * | | + * Creates when config present, | + * subscription group active, and | + * providing app is carrier privileged Notifies of safe + * | mode state changes + * v | + * +-----------------------------------------------------------------------+ + * | VcnInstance | + * | | + * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) | + * | and overall safe-mode | + * +-----------------------------------------------------------------------+ + * | 1:N ^ + * Creates to fulfill | + * NetworkRequest(s), tears Notifies of VcnTunnel + * down when no longer needed teardown (e.g. Network reaped) + * | and safe-mode timer changes + * v | + * +-----------------------------------------------------------------------+ + * | VcnTunnel | + * | | + * | Manages a single (IKEv2) tunnel session and NetworkAgent, | + * | handles mobility events, (IPsec) Tunnel setup and safe-mode timers | + * +-----------------------------------------------------------------------+ + * | 1:1 ^ + * | | + * Creates upon instantiation Notifies of changes in + * | selected underlying network + * | or its properties + * v | + * +-----------------------------------------------------------------------+ + * | UnderlyingNetworkTracker | + * | | + * | Manages lifecycle of underlying physical networks, filing requests to | + * | bring them up, and releasing them as they become no longer necessary | + * +-----------------------------------------------------------------------+ + * </pre> + * + * @hide + */ +public class VcnManagementService extends IVcnManagementService.Stub { + @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); + + public static final boolean VDBG = false; // STOPSHIP: if true + + /* Binder context for this service */ + @NonNull private final Context mContext; + @NonNull private final Dependencies mDeps; + + private VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { + mContext = requireNonNull(context, "Missing context"); + mDeps = requireNonNull(deps, "Missing dependencies"); + } + + // Package-visibility for SystemServer to create instances. + static VcnManagementService create(@NonNull Context context) { + return new VcnManagementService(context, new Dependencies()); + } + + private static class Dependencies {} + + /** Notifies the VcnManagementService that external dependencies can be set up */ + public void systemReady() {} +} diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index afddd650c46c..74fba0441be8 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -1794,25 +1794,20 @@ public class VibratorService extends IVibratorService.Stub mWakeLock.setWorkSource(mTmpWorkSource); } - private long delayLocked(long duration) { + private void delayLocked(long wakeUpTime) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked"); try { - long durationRemaining = duration; - if (duration > 0) { - final long bedtime = duration + SystemClock.uptimeMillis(); - do { - try { - this.wait(durationRemaining); - } - catch (InterruptedException e) { } - if (mForceStop) { - break; - } - durationRemaining = bedtime - SystemClock.uptimeMillis(); - } while (durationRemaining > 0); - return duration - durationRemaining; + long durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); + while (durationRemaining > 0) { + try { + this.wait(durationRemaining); + } catch (InterruptedException e) { + } + if (mForceStop) { + break; + } + durationRemaining = wakeUpTime - SystemClock.uptimeMillis(); } - return 0; } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -1846,7 +1841,8 @@ public class VibratorService extends IVibratorService.Stub final int repeat = mWaveform.getRepeatIndex(); int index = 0; - long onDuration = 0; + long nextStepStartTime = SystemClock.uptimeMillis(); + long nextVibratorStopTime = 0; while (!mForceStop) { if (index < len) { final int amplitude = amplitudes[index]; @@ -1855,27 +1851,33 @@ public class VibratorService extends IVibratorService.Stub continue; } if (amplitude != 0) { - if (onDuration <= 0) { + long now = SystemClock.uptimeMillis(); + if (nextVibratorStopTime <= now) { // Telling the vibrator to start multiple times usually causes // effects to feel "choppy" because the motor resets at every on // command. Instead we figure out how long our next "on" period // is going to be, tell the motor to stay on for the full // duration, and then wake up to change the amplitude at the // appropriate intervals. - onDuration = getTotalOnDuration(timings, amplitudes, index - 1, - repeat); + long onDuration = getTotalOnDuration( + timings, amplitudes, index - 1, repeat); mVibration.effect = VibrationEffect.createOneShot( onDuration, amplitude); doVibratorOn(mVibration); + nextVibratorStopTime = now + onDuration; } else { + // Vibrator is already ON, so just change its amplitude. doVibratorSetAmplitude(amplitude); } } - long waitTime = delayLocked(duration); - if (amplitude != 0) { - onDuration -= waitTime; - } + // We wait until the time this waveform step was supposed to end, + // calculated from the time it was supposed to start. All start times + // are calculated from the waveform original start time by adding the + // input durations. Any scheduling or processing delay should not affect + // this step's perceived total duration. They will be amortized here. + nextStepStartTime += duration; + delayLocked(nextStepStartTime); } else if (repeat < 0) { break; } else { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index 5219df4a841d..4b59112b3524 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -43,7 +43,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) throws RemoteException { mFingerprintService.prepareForAuthentication(token, operationId, userId, sensorReceiver, - opPackageName, cookie, callingUid, callingPid, callingUserId, null /* surface */); + opPackageName, cookie, callingUid, callingPid, callingUserId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 9ac12ed11ded..c88247f59871 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -46,7 +46,6 @@ import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; -import android.os.NativeHandle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -56,7 +55,6 @@ import android.util.EventLog; import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import android.view.Surface; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -156,8 +154,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName, - Surface surface) { + final IFingerprintServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -167,7 +164,7 @@ public class FingerprintService extends SystemService { } provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, - receiver, opPackageName, surface); + receiver, opPackageName); } @Override // Binder call @@ -185,8 +182,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void authenticate(final IBinder token, final long operationId, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName, - final Surface surface) { + final IFingerprintServiceReceiver receiver, final String opPackageName) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -233,8 +229,7 @@ public class FingerprintService extends SystemService { @Override public void detectFingerprint(final IBinder token, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName, - final Surface surface) { + final IFingerprintServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName); @@ -255,15 +250,14 @@ public class FingerprintService extends SystemService { } provider.second.scheduleFingerDetect(provider.first, token, userId, - new ClientMonitorCallbackConverter(receiver), opPackageName, surface, + new ClientMonitorCallbackConverter(receiver), opPackageName, BiometricsProtoEnums.CLIENT_KEYGUARD); } @Override // Binder call public void prepareForAuthentication(IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, - int cookie, int callingUid, int callingPid, int callingUserId, - Surface surface) { + int cookie, int callingUid, int callingPid, int callingUserId) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -729,6 +723,4 @@ public class FingerprintService extends SystemService { } return appOpsOk; } - - private native NativeHandle convertSurfaceToNativeHandle(Surface surface); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 1ed66a247bd0..c13a6569b16a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -18,15 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint; import android.annotation.NonNull; import android.annotation.Nullable; -import android.hardware.fingerprint.Fingerprint; import android.hardware.biometrics.ITestSession; +import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.util.proto.ProtoOutputStream; -import android.view.Surface; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutTracker; @@ -62,7 +61,8 @@ public interface ServiceProvider { */ boolean containsSensor(int sensorId); - @NonNull List<FingerprintSensorPropertiesInternal> getSensorProperties(); + @NonNull + List<FingerprintSensorPropertiesInternal> getSensorProperties(); void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); @@ -73,14 +73,13 @@ public interface ServiceProvider { @NonNull String opPackageName, long challenge); void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, - @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - @Nullable Surface surface); + @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName); void cancelEnrollment(int sensorId, @NonNull IBinder token); void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, - @Nullable Surface surface, int statsClient); + int statsClient); void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @@ -100,9 +99,11 @@ public interface ServiceProvider { void rename(int sensorId, int fingerId, int userId, @NonNull String name); - @NonNull List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId); + @NonNull + List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId); - @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId); + @LockoutTracker.LockoutMode + int getLockoutModeForUser(int sensorId, int userId); long getAuthenticatorId(int sensorId, int userId); @@ -118,5 +119,6 @@ public interface ServiceProvider { void dumpInternal(int sensorId, @NonNull PrintWriter pw); - @NonNull ITestSession createTestSession(int sensorId, @NonNull String opPackageName); + @NonNull + ITestSession createTestSession(int sensorId, @NonNull String opPackageName); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java index 6bb40e6630bf..973132533661 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -121,7 +121,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), null /* surface */); + mContext.getOpPackageName()); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 4d07f583fdf5..0ebfe9381d15 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -40,7 +40,6 @@ import android.os.UserManager; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; -import android.view.Surface; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; @@ -341,7 +340,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, - @NonNull String opPackageName, @Nullable Surface surface) { + @NonNull String opPackageName) { mHandler.post(() -> { final IFingerprint daemon = getHalInstance(); if (daemon == null) { @@ -387,7 +386,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, - @Nullable Surface surface, int statsClient) { + int statsClient) { mHandler.post(() -> { final IFingerprint daemon = getHalInstance(); if (daemon == null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index e0ea99077d51..65ce34d31b61 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -122,7 +122,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), null /* surface */); + mContext.getOpPackageName()); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 241c911ce9ab..a806e3f61bc8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -46,7 +46,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import android.view.Surface; import com.android.internal.R; import com.android.internal.util.FrameworkStatsLog; @@ -539,8 +538,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, - @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - @Nullable Surface surface) { + @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -571,7 +569,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, - @Nullable Surface surface, int statsClient) { + int statsClient) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 4c2b1e34fa78..aebcdf19ecf4 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -201,6 +201,7 @@ public class Vpn { private final Context mContext; @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; + private int mLegacyState; @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; @@ -415,6 +416,7 @@ public class Vpn { Log.wtf(TAG, "Problem registering observer", e); } + mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE, "" /* subtypeName */); mNetworkCapabilities = new NetworkCapabilities(); @@ -440,6 +442,7 @@ public class Vpn { @VisibleForTesting protected void updateState(DetailedState detailedState, String reason) { if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason); + mLegacyState = LegacyVpnInfo.stateFromNetworkInfo(detailedState); mNetworkInfo.setDetailedState(detailedState, reason, null); if (mNetworkAgent != null) { mNetworkAgent.sendNetworkInfo(mNetworkInfo); @@ -1243,6 +1246,7 @@ public class Vpn { // behaves the same as when it uses the default network. mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + mLegacyState = LegacyVpnInfo.STATE_CONNECTING; mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null); NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); @@ -2265,7 +2269,7 @@ public class Vpn { final LegacyVpnInfo info = new LegacyVpnInfo(); info.key = mConfig.user; - info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo); + info.state = mLegacyState; if (mNetworkInfo.isConnected()) { info.intent = mStatusIntent; } diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 73f788901dc9..5e1df27167c2 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.android.server.wm.utils.RotationAnimationUtils.hasProtectedContent; + import android.content.Context; import android.graphics.SurfaceTexture; import android.hardware.display.DisplayManagerInternal; @@ -35,7 +37,6 @@ import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; -import android.view.SurfaceSession; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; @@ -75,6 +76,7 @@ final class ColorFade { private static final int EGL_GL_COLORSPACE_KHR = 0x309D; private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490; + private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0; private final int mDisplayId; @@ -87,7 +89,6 @@ final class ColorFade { private int mDisplayLayerStack; // layer stack associated with primary display private int mDisplayWidth; // real width, not rotated private int mDisplayHeight; // real height, not rotated - private SurfaceSession mSurfaceSession; private SurfaceControl mSurfaceControl; private Surface mSurface; private NaturalSurfaceLayout mSurfaceLayout; @@ -97,7 +98,8 @@ final class ColorFade { private EGLSurface mEglSurface; private boolean mSurfaceVisible; private float mSurfaceAlpha; - private boolean mIsWideColor; + private boolean mLastWasWideColor; + private boolean mLastWasProtectedContent; // Texture names. We only use one texture, which contains the screenshot. private final int[] mTexNames = new int[1]; @@ -157,9 +159,28 @@ final class ColorFade { mDisplayWidth = displayInfo.getNaturalWidth(); mDisplayHeight = displayInfo.getNaturalHeight(); - // Prepare the surface for drawing. - if (!(createSurface() && createEglContext() && createEglSurface() && - captureScreenshotTextureAndSetViewport())) { + final IBinder token = SurfaceControl.getInternalDisplayToken(); + if (token == null) { + Slog.e(TAG, + "Failed to take screenshot because internal display is disconnected"); + return false; + } + boolean isWideColor = SurfaceControl.getActiveColorMode(token) + == Display.COLOR_MODE_DISPLAY_P3; + + // Set mPrepared here so if initialization fails, resources can be cleaned up. + mPrepared = true; + + SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = captureScreen(); + if (hardwareBuffer == null) { + dismiss(); + return false; + } + + boolean isProtected = hasProtectedContent(hardwareBuffer.getHardwareBuffer()); + if (!(createSurfaceControl(hardwareBuffer.containsSecureLayers()) + && createEglContext(isProtected) && createEglSurface(isProtected, isWideColor) + && setScreenshotTextureAndSetViewport(hardwareBuffer))) { dismiss(); return false; } @@ -180,7 +201,8 @@ final class ColorFade { // Done. mCreatedResources = true; - mPrepared = true; + mLastWasProtectedContent = isProtected; + mLastWasWideColor = isWideColor; // Dejanking optimization. // Some GL drivers can introduce a lot of lag in the first few frames as they @@ -466,7 +488,8 @@ final class ColorFade { mProjMatrix[15] = 1f; } - private boolean captureScreenshotTextureAndSetViewport() { + private boolean setScreenshotTextureAndSetViewport( + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) { if (!attachEglContext()) { return false; } @@ -482,27 +505,9 @@ final class ColorFade { final SurfaceTexture st = new SurfaceTexture(mTexNames[0]); final Surface s = new Surface(st); try { - final IBinder token = SurfaceControl.getInternalDisplayToken(); - if (token == null) { - Slog.e(TAG, - "Failed to take screenshot because internal display is disconnected"); - return false; - } - - mIsWideColor = SurfaceControl.getActiveColorMode(token) - == Display.COLOR_MODE_DISPLAY_P3; - SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = - mDisplayManagerInternal.systemScreenshot(mDisplayId); - if (screenshotBuffer == null) { - Slog.e(TAG, "Failed to take screenshot. Buffer is null"); - return false; - } s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace()); - if (screenshotBuffer.containsSecureLayers()) { - mTransaction.setSecure(mSurfaceControl, true).apply(); - } st.updateTexImage(); st.getTransformMatrix(mTexMatrix); } finally { @@ -535,7 +540,52 @@ final class ColorFade { } } - private boolean createEglContext() { + private SurfaceControl.ScreenshotHardwareBuffer captureScreen() { + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = + mDisplayManagerInternal.systemScreenshot(mDisplayId); + if (screenshotBuffer == null) { + Slog.e(TAG, "Failed to take screenshot. Buffer is null"); + return null; + } + return screenshotBuffer; + } + + private boolean createSurfaceControl(boolean isSecure) { + if (mSurfaceControl != null) { + mTransaction.setSecure(mSurfaceControl, isSecure).apply(); + return true; + } + + try { + final SurfaceControl.Builder builder = new SurfaceControl.Builder() + .setName("ColorFade") + .setSecure(isSecure) + .setCallsite("ColorFade.createSurface"); + if (mMode == MODE_FADE) { + builder.setColorLayer(); + } else { + builder.setBufferSize(mDisplayWidth, mDisplayHeight); + } + mSurfaceControl = builder.build(); + } catch (OutOfResourcesException ex) { + Slog.e(TAG, "Unable to create surface.", ex); + return false; + } + + mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack); + mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight); + mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, mDisplayId, + mSurfaceControl); + mSurfaceLayout.onDisplayTransaction(mTransaction); + mTransaction.apply(); + + mSurface = new Surface(); + mSurface.copyFrom(mSurfaceControl); + + return true; + } + + private boolean createEglContext(boolean isProtected) { if (mEglDisplay == null) { mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); if (mEglDisplay == EGL14.EGL_NO_DISPLAY) { @@ -576,13 +626,25 @@ final class ColorFade { mEglConfig = eglConfigs[0]; } + // The old context needs to be destroyed if the protected flag has changed. The context will + // be recreated based on the protected flag + if (mEglContext != null && isProtected != mLastWasProtectedContent) { + EGL14.eglDestroyContext(mEglDisplay, mEglContext); + mEglContext = null; + } + if (mEglContext == null) { int[] eglContextAttribList = new int[] { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, + EGL14.EGL_NONE, EGL14.EGL_NONE, EGL14.EGL_NONE }; - mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, - EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0); + if (isProtected) { + eglContextAttribList[2] = EGL_PROTECTED_CONTENT_EXT; + eglContextAttribList[3] = EGL14.EGL_TRUE; + } + mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT, + eglContextAttribList, 0); if (mEglContext == null) { logEglError("eglCreateContext"); return false; @@ -591,53 +653,34 @@ final class ColorFade { return true; } - private boolean createSurface() { - if (mSurfaceSession == null) { - mSurfaceSession = new SurfaceSession(); - } - - if (mSurfaceControl == null) { - try { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession) - .setName("ColorFade") - .setCallsite("ColorFade.createSurface"); - if (mMode == MODE_FADE) { - builder.setColorLayer(); - } else { - builder.setBufferSize(mDisplayWidth, mDisplayHeight); - } - mSurfaceControl = builder.build(); - } catch (OutOfResourcesException ex) { - Slog.e(TAG, "Unable to create surface.", ex); - return false; - } - - mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack); - mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight); - mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, - mDisplayId, mSurfaceControl); - mSurfaceLayout.onDisplayTransaction(mTransaction); - mTransaction.apply(); - - mSurface = new Surface(); - mSurface.copyFrom(mSurfaceControl); - + private boolean createEglSurface(boolean isProtected, boolean isWideColor) { + // The old surface needs to be destroyed if either the protected flag or wide color flag has + // changed. The surface will be recreated based on the new flags. + boolean didContentAttributesChange = + isProtected != mLastWasProtectedContent || isWideColor != mLastWasWideColor; + if (mEglSurface != null && didContentAttributesChange) { + EGL14.eglDestroySurface(mEglDisplay, mEglSurface); + mEglSurface = null; } - return true; - } - private boolean createEglSurface() { if (mEglSurface == null) { int[] eglSurfaceAttribList = new int[] { EGL14.EGL_NONE, EGL14.EGL_NONE, + EGL14.EGL_NONE, + EGL14.EGL_NONE, EGL14.EGL_NONE }; + int index = 0; // If the current display is in wide color, then so is the screenshot. - if (mIsWideColor) { - eglSurfaceAttribList[0] = EGL_GL_COLORSPACE_KHR; - eglSurfaceAttribList[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; + if (isWideColor) { + eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_KHR; + eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; + } + if (isProtected) { + eglSurfaceAttribList[index++] = EGL_PROTECTED_CONTENT_EXT; + eglSurfaceAttribList[index] = EGL14.EGL_TRUE; } // turn our SurfaceControl into a Surface mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index c4dfcf5c3165..6a3e7b7a0ecd 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1293,6 +1293,7 @@ public final class DisplayManagerService extends SystemService { .setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight()) .setUseIdentityTransform(true) .setCaptureSecureLayers(true) + .setAllowProtected(true) .build(); return SurfaceControl.captureDisplay(captureArgs); } diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 71b377cc2f11..c013145b5269 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -16,6 +16,7 @@ package com.android.server.display; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -50,11 +51,14 @@ import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; + /** * The DisplayModeDirector is responsible for determining what modes are allowed to be automatically * picked by the system based on system-wide and display-specific configuration. @@ -99,6 +103,29 @@ public class DisplayModeDirector { private boolean mAlwaysRespectAppRequest; + @IntDef(prefix = {"SWITCHING_TYPE_"}, value = { + SWITCHING_TYPE_NONE, + SWITCHING_TYPE_WITHIN_GROUPS, + SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SwitchingType {} + + // No mode switching will happen. + public static final int SWITCHING_TYPE_NONE = 0; + // Allow only refresh rate switching between modes in the same configuration group. This way + // only switches without visual interruptions for the user will be allowed. + public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; + // Allow refresh rate switching between all refresh rates even if the switch with have visual + // interruptions for the user. + public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; + + /** + * The allowed refresh rate switching type. This is used by SurfaceFlinger. + */ + @SwitchingType + private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS; + public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); @@ -290,14 +317,37 @@ public class DisplayModeDirector { appRequestSummary.maxRefreshRate)); } + // If the application requests a given mode with preferredModeId function, it will be + // stored as baseModeId. int baseModeId = defaultMode.getModeId(); if (availableModes.length > 0) { baseModeId = availableModes[0]; } - // filterModes function is going to filter the modes based on the voting system. If - // the application requests a given mode with preferredModeId function, it will be - // stored as baseModeId. + if (mModeSwitchingType == SWITCHING_TYPE_NONE) { + Display.Mode baseMode = null; + for (Display.Mode mode : modes) { + if (mode.getModeId() == baseModeId) { + baseMode = mode; + break; + } + } + if (baseMode == null) { + // This should never happen. + throw new IllegalStateException( + "The base mode with id " + baseModeId + + " is not in the list of supported modes."); + } + float fps = baseMode.getRefreshRate(); + return new DesiredDisplayModeSpecs(baseModeId, + /*allowGroupSwitching */ false, + new RefreshRateRange(fps, fps), + new RefreshRateRange(fps, fps)); + } + + boolean allowGroupSwitching = + mModeSwitchingType == SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; return new DesiredDisplayModeSpecs(baseModeId, + allowGroupSwitching, new RefreshRateRange( primarySummary.minRefreshRate, primarySummary.maxRefreshRate), new RefreshRateRange( @@ -385,6 +435,26 @@ public class DisplayModeDirector { } /** + * Sets the display mode switching type. + * @param type + */ + public void setModeSwitchingType(@SwitchingType int type) { + synchronized (mLock) { + mModeSwitchingType = type; + } + } + + /** + * Returns the display mode switching type. + */ + @SwitchingType + public int getModeSwitchingType() { + synchronized (mLock) { + return mModeSwitchingType; + } + } + + /** * Print the object's state and debug information into the given stream. * * @param pw The stream to dump information to. @@ -416,6 +486,7 @@ public class DisplayModeDirector { pw.println(" " + Vote.priorityToString(p) + " -> " + vote); } } + pw.println(" mModeSwitchingType: " + switchingTypeToString(mModeSwitchingType)); pw.println(" mAlwaysRespectAppRequest: " + mAlwaysRespectAppRequest); mSettingsObserver.dumpLocked(pw); mAppRequestObserver.dumpLocked(pw); @@ -472,7 +543,6 @@ public class DisplayModeDirector { } private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) { - int index = mVotesByDisplay.indexOfKey(displayId); if (mVotesByDisplay.indexOfKey(displayId) >= 0) { return mVotesByDisplay.get(displayId); } else { @@ -482,6 +552,19 @@ public class DisplayModeDirector { } } + private static String switchingTypeToString(@SwitchingType int type) { + switch (type) { + case SWITCHING_TYPE_NONE: + return "SWITCHING_TYPE_NONE"; + case SWITCHING_TYPE_WITHIN_GROUPS: + return "SWITCHING_TYPE_WITHIN_GROUPS"; + case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS: + return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS"; + default: + return "Unknown SwitchingType " + type; + } + } + @VisibleForTesting void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) { mSupportedModesByDisplay = supportedModesByDisplay; @@ -631,18 +714,26 @@ public class DisplayModeDirector { * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here. */ public static final class DesiredDisplayModeSpecs { + /** * Base mode ID. This is what system defaults to for all other settings, or * if the refresh rate range is not available. */ public int baseModeId; + + /** + * If true this will allow switching between modes in different display configuration + * groups. This way the user may see visual interruptions when the display mode changes. + */ + public boolean allowGroupSwitching; + /** * The primary refresh rate range. */ public final RefreshRateRange primaryRefreshRateRange; /** * The app request refresh rate range. Lower priority considerations won't be included in - * this range, allowing surface flinger to consider additional refresh rates for apps that + * this range, allowing SurfaceFlinger to consider additional refresh rates for apps that * call setFrameRate(). This range will be greater than or equal to the primary refresh rate * range, never smaller. */ @@ -654,9 +745,11 @@ public class DisplayModeDirector { } public DesiredDisplayModeSpecs(int baseModeId, + boolean allowGroupSwitching, @NonNull RefreshRateRange primaryRefreshRateRange, @NonNull RefreshRateRange appRequestRefreshRateRange) { this.baseModeId = baseModeId; + this.allowGroupSwitching = allowGroupSwitching; this.primaryRefreshRateRange = primaryRefreshRateRange; this.appRequestRefreshRateRange = appRequestRefreshRateRange; } @@ -666,10 +759,12 @@ public class DisplayModeDirector { */ @Override public String toString() { - return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]" + return String.format("baseModeId=%d allowGroupSwitching=%b" + + " primaryRefreshRateRange=[%.0f %.0f]" + " appRequestRefreshRateRange=[%.0f %.0f]", - baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max, - appRequestRefreshRateRange.min, appRequestRefreshRateRange.max); + baseModeId, allowGroupSwitching, primaryRefreshRateRange.min, + primaryRefreshRateRange.max, appRequestRefreshRateRange.min, + appRequestRefreshRateRange.max); } /** * Checks whether the two objects have the same values. @@ -689,6 +784,9 @@ public class DisplayModeDirector { if (baseModeId != desiredDisplayModeSpecs.baseModeId) { return false; } + if (allowGroupSwitching != desiredDisplayModeSpecs.allowGroupSwitching) { + return false; + } if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) { return false; } @@ -701,7 +799,8 @@ public class DisplayModeDirector { @Override public int hashCode() { - return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange); + return Objects.hash(baseModeId, allowGroupSwitching, primaryRefreshRateRange, + appRequestRefreshRateRange); } /** @@ -709,6 +808,7 @@ public class DisplayModeDirector { */ public void copyFrom(DesiredDisplayModeSpecs other) { baseModeId = other.baseModeId; + allowGroupSwitching = other.allowGroupSwitching; primaryRefreshRateRange.min = other.primaryRefreshRateRange.min; primaryRefreshRateRange.max = other.primaryRefreshRateRange.max; appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min; diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 86691848c363..9437677d8ab8 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -817,6 +817,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this, getDisplayTokenLocked(), new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId, + mDisplayModeSpecs.allowGroupSwitching, mDisplayModeSpecs.primaryRefreshRateRange.min, mDisplayModeSpecs.primaryRefreshRateRange.max, mDisplayModeSpecs.appRequestRefreshRateRange.min, diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 6ff661b1637e..5fddae53d632 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -66,10 +66,10 @@ final class Constants { public static final int ADDR_PLAYBACK_3 = 11; /** Logical address reserved for future usage */ - public static final int ADDR_RESERVED_1 = 12; + public static final int ADDR_BACKUP_1 = 12; /** Logical address reserved for future usage */ - public static final int ADDR_RESERVED_2 = 13; + public static final int ADDR_BACKUP_2 = 13; /** Logical address for TV other than the one assigned with {@link #ADDR_TV} */ public static final int ADDR_SPECIFIC_USE = 14; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 652bf5a7a67f..e906a7c8132c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -37,6 +37,7 @@ import com.android.server.hdmi.cec.config.XmlParser; import org.xmlpull.v1.XmlPullParserException; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -81,7 +82,7 @@ public class HdmiCecConfig { @NonNull private final Context mContext; @NonNull private final StorageAdapter mStorageAdapter; - @Nullable private final CecSettings mProductConfig; + @Nullable private final CecSettings mSystemConfig; @Nullable private final CecSettings mVendorOverride; /** @@ -129,14 +130,14 @@ public class HdmiCecConfig { @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, - @Nullable CecSettings productConfig, + @Nullable CecSettings systemConfig, @Nullable CecSettings vendorOverride) { mContext = context; mStorageAdapter = storageAdapter; - mProductConfig = productConfig; + mSystemConfig = systemConfig; mVendorOverride = vendorOverride; - if (mProductConfig == null) { - Slog.i(TAG, "CEC master configuration XML missing."); + if (mSystemConfig == null) { + Slog.i(TAG, "CEC system configuration XML missing."); } if (mVendorOverride == null) { Slog.i(TAG, "CEC OEM configuration override XML missing."); @@ -145,7 +146,7 @@ public class HdmiCecConfig { HdmiCecConfig(@NonNull Context context) { this(context, new StorageAdapter(), - readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(), + readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(), ETC_DIR, CONFIG_FILE)), readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(), ETC_DIR, CONFIG_FILE))); @@ -168,9 +169,32 @@ public class HdmiCecConfig { return null; } + @NonNull + @VisibleForTesting + static HdmiCecConfig createFromStrings(@NonNull Context context, + @NonNull StorageAdapter storageAdapter, + @Nullable String systemConfigXml, + @Nullable String vendorOverrideXml) { + CecSettings systemConfig = null; + CecSettings vendorOverride = null; + try { + if (systemConfigXml != null) { + systemConfig = XmlParser.read( + new ByteArrayInputStream(systemConfigXml.getBytes())); + } + if (vendorOverrideXml != null) { + vendorOverride = XmlParser.read( + new ByteArrayInputStream(vendorOverrideXml.getBytes())); + } + } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { + Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e); + } + return new HdmiCecConfig(context, storageAdapter, systemConfig, vendorOverride); + } + @Nullable private Setting getSetting(@NonNull String name) { - if (mProductConfig == null) { + if (mSystemConfig == null) { return null; } if (mVendorOverride != null) { @@ -181,8 +205,8 @@ public class HdmiCecConfig { } } } - // If not found, try the product config. - for (Setting setting : mProductConfig.getSetting()) { + // If not found, try the system config. + for (Setting setting : mSystemConfig.getSetting()) { if (setting.getName().equals(name)) { return setting; } @@ -254,11 +278,11 @@ public class HdmiCecConfig { * Returns a list of all settings based on the XML metadata. */ public @CecSettingName List<String> getAllSettings() { - if (mProductConfig == null) { + if (mSystemConfig == null) { return new ArrayList<String>(); } List<String> allSettings = new ArrayList<String>(); - for (Setting setting : mProductConfig.getSetting()) { + for (Setting setting : mSystemConfig.getSetting()) { allSettings.add(setting.getName()); } return allSettings; @@ -268,12 +292,12 @@ public class HdmiCecConfig { * Returns a list of user-modifiable settings based on the XML metadata. */ public @CecSettingName List<String> getUserSettings() { - if (mProductConfig == null) { + if (mSystemConfig == null) { return new ArrayList<String>(); } Set<String> userSettings = new HashSet<String>(); - // First read from the product config. - for (Setting setting : mProductConfig.getSetting()) { + // First read from the system config. + for (Setting setting : mSystemConfig.getSetting()) { if (setting.getUserConfigurable()) { userSettings.add(setting.getName()); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index efe730231d36..19dc017a35d5 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -85,6 +85,8 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; + private static final int MAX_DEDICATED_ADDRESS = 11; + private static final int MAX_HDMI_MESSAGE_HISTORY = 250; private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; @@ -104,7 +106,7 @@ final class HdmiCecController { private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() { @Override public boolean test(Integer address) { - return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM; + return HdmiUtils.isEligibleAddressForDevice(Constants.ADDR_AUDIO_SYSTEM, address); } }; @@ -195,42 +197,46 @@ final class HdmiCecController { }); } + /** + * Address allocation will check the following addresses (in order): + * <ul> + * <li>Given preferred logical address (if the address is valid for the given device + * type)</li> + * <li>All dedicated logical addresses for the given device type</li> + * <li>Backup addresses, if valid for the given device type</li> + * </ul> + */ @IoThreadOnly private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress, final AllocateAddressCallback callback) { assertRunOnIoThread(); - int startAddress = preferredAddress; - // If preferred address is "unregistered", start address will be the smallest - // address matched with the given device type. - if (preferredAddress == Constants.ADDR_UNREGISTERED) { - for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { - if (deviceType == HdmiUtils.getTypeFromAddress(i)) { - startAddress = i; - break; - } + List<Integer> logicalAddressesToPoll = new ArrayList<>(); + if (HdmiUtils.isEligibleAddressForDevice(deviceType, preferredAddress)) { + logicalAddressesToPoll.add(preferredAddress); + } + for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { + if (!logicalAddressesToPoll.contains(i) && HdmiUtils.isEligibleAddressForDevice( + deviceType, i) && HdmiUtils.isEligibleAddressForCecVersion( + mService.getCecVersion(), i)) { + logicalAddressesToPoll.add(i); } } int logicalAddress = Constants.ADDR_UNREGISTERED; - // Iterates all possible addresses which has the same device type. - for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { - int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; - if (curAddress != Constants.ADDR_UNREGISTERED - && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) { - boolean acked = false; - for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) { - if (sendPollMessage(curAddress, curAddress, 1)) { - acked = true; - break; - } - } - // If sending <Polling Message> failed, it becomes new logical address for the - // device because no device uses it as logical address of the device. - if (!acked) { - logicalAddress = curAddress; + for (Integer logicalAddressToPoll : logicalAddressesToPoll) { + boolean acked = false; + for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) { + if (sendPollMessage(logicalAddressToPoll, logicalAddressToPoll, 1)) { + acked = true; break; } } + // If sending <Polling Message> failed, it becomes new logical address for the + // device because no device uses it as logical address of the device. + if (!acked) { + logicalAddress = logicalAddressToPoll; + break; + } } final int assignedAddress = logicalAddress; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 4325f797416e..960510693f6a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -246,6 +246,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { // Indicates if current device is the active source or not @ServiceThreadOnly protected boolean isActiveSource() { + if (getDeviceInfo() == null) { + return false; + } + return getActiveSource().equals(getDeviceInfo().getLogicalAddress(), getDeviceInfo().getPhysicalAddress()); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 93cdca2d0c83..c2e80caadd3b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -1368,9 +1368,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private boolean checkRecorder(int recorderAddress) { HdmiDeviceInfo device = mService.getHdmiCecNetwork().getCecDeviceInfo(recorderAddress); - return (device != null) - && (HdmiUtils.getTypeFromAddress(recorderAddress) - == HdmiDeviceInfo.DEVICE_RECORDER); + return (device != null) && (HdmiUtils.isEligibleAddressForDevice( + HdmiDeviceInfo.DEVICE_RECORDER, recorderAddress)); } private boolean checkRecordSource(byte[] recordSource) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index d4593afe18fe..7d766285bdfa 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -152,8 +152,10 @@ public class HdmiCecMessageValidator { addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, new AsciiValidator(1, 14), DEST_DIRECT); // Messages for the Device Menu Control. - addValidationInfo(Constants.MESSAGE_MENU_REQUEST, oneByteValidator, DEST_DIRECT); - addValidationInfo(Constants.MESSAGE_MENU_STATUS, oneByteValidator, DEST_DIRECT); + addValidationInfo( + Constants.MESSAGE_MENU_REQUEST, new OneByteRangeValidator(0x00, 0x02), DEST_DIRECT); + addValidationInfo( + Constants.MESSAGE_MENU_STATUS, new OneByteRangeValidator(0x00, 0x01), DEST_DIRECT); // Messages for the Remote Control Passthrough. // TODO: Parse the first parameter and determine if it can have the next parameter. @@ -161,7 +163,10 @@ public class HdmiCecMessageValidator { new VariableLengthValidator(1, 2), DEST_DIRECT); // Messages for the Power Status. - addValidationInfo(Constants.MESSAGE_REPORT_POWER_STATUS, oneByteValidator, DEST_DIRECT); + addValidationInfo( + Constants.MESSAGE_REPORT_POWER_STATUS, + new OneByteRangeValidator(0x00, 0x03), + DEST_DIRECT); // Messages for the General Protocol. addValidationInfo(Constants.MESSAGE_FEATURE_ABORT, @@ -173,12 +178,20 @@ public class HdmiCecMessageValidator { new FixedLengthValidator(3), DEST_DIRECT); addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, oneByteValidator, DEST_DIRECT); - addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, oneByteValidator, DEST_ALL); - addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, - oneByteValidator, DEST_DIRECT); + addValidationInfo( + Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, + new OneByteRangeValidator(0x00, 0x01), + DEST_ALL); + addValidationInfo( + Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, + new OneByteRangeValidator(0x00, 0x01), + DEST_DIRECT); // Messages for the Audio Rate Control. - addValidationInfo(Constants.MESSAGE_SET_AUDIO_RATE, oneByteValidator, DEST_DIRECT); + addValidationInfo( + Constants.MESSAGE_SET_AUDIO_RATE, + new OneByteRangeValidator(0x00, 0x06), + DEST_DIRECT); // All Messages for the ARC have no parameters. @@ -441,4 +454,22 @@ public class HdmiCecMessageValidator { && isValidAsciiString(params, 1, 14)); } } + + /** Check if the given parameters are one byte parameters and within range. */ + private class OneByteRangeValidator implements ParameterValidator { + private final int mMinValue, mMaxValue; + + OneByteRangeValidator(int minValue, int maxValue) { + mMinValue = minValue; + mMaxValue = maxValue; + } + + @Override + public int isValid(byte[] params) { + if (params.length < 1) { + return ERROR_PARAMETER_SHORT; + } + return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue)); + } + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index b4a765e10fca..da4c6f115d55 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1561,7 +1561,7 @@ public class HdmiControlService extends SystemService { new HdmiDeviceInfo(activeSource.logicalAddress, activeSource.physicalAddress, pathToPortId(activeSource.physicalAddress), - HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0, + HdmiUtils.getTypeFromAddress(activeSource.logicalAddress).get(0), 0, HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress)) : new HdmiDeviceInfo(activeSource.physicalAddress, diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index a6951ef1958f..45aaa73b757b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -16,6 +16,10 @@ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.ADDR_BACKUP_1; +import static com.android.server.hdmi.Constants.ADDR_BACKUP_2; +import static com.android.server.hdmi.Constants.ADDR_TV; + import android.annotation.Nullable; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; @@ -29,6 +33,8 @@ import com.android.server.hdmi.Constants.AudioCodec; import com.android.server.hdmi.Constants.FeatureOpcode; import com.android.server.hdmi.Constants.PathRelationship; +import com.google.android.collect.Lists; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -37,6 +43,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -48,23 +55,38 @@ final class HdmiUtils { private static final String TAG = "HdmiUtils"; - private static final int[] ADDRESS_TO_TYPE = { - HdmiDeviceInfo.DEVICE_TV, // ADDR_TV - HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_1 - HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_2 - HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_1 - HdmiDeviceInfo.DEVICE_PLAYBACK, // ADDR_PLAYBACK_1 - HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, // ADDR_AUDIO_SYSTEM - HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_2 - HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_3 - HdmiDeviceInfo.DEVICE_PLAYBACK, // ADDR_PLAYBACK_2 - HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_3 - HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_4 - HdmiDeviceInfo.DEVICE_PLAYBACK, // ADDR_PLAYBACK_3 - HdmiDeviceInfo.DEVICE_RESERVED, - HdmiDeviceInfo.DEVICE_RESERVED, - HdmiDeviceInfo.DEVICE_TV, // ADDR_SPECIFIC_USE - }; + private static final Map<Integer, List<Integer>> ADDRESS_TO_TYPE = + new HashMap<Integer, List<Integer>>() { + { + put(Constants.ADDR_TV, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV)); + put(Constants.ADDR_RECORDER_1, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)); + put(Constants.ADDR_RECORDER_2, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)); + put(Constants.ADDR_TUNER_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); + put(Constants.ADDR_PLAYBACK_1, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)); + put(Constants.ADDR_AUDIO_SYSTEM, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)); + put(Constants.ADDR_TUNER_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); + put(Constants.ADDR_TUNER_3, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); + put(Constants.ADDR_PLAYBACK_2, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)); + put(Constants.ADDR_RECORDER_3, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)); + put(Constants.ADDR_TUNER_4, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); + put(Constants.ADDR_PLAYBACK_3, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)); + put(Constants.ADDR_BACKUP_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, + HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER, + HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)); + put(Constants.ADDR_BACKUP_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, + HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER, + HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)); + put(Constants.ADDR_SPECIFIC_USE, Lists.newArrayList(ADDR_TV)); + put(Constants.ADDR_UNREGISTERED, Collections.emptyList()); + } + }; private static final String[] DEFAULT_NAMES = { "TV", @@ -79,8 +101,8 @@ final class HdmiUtils { "Recorder_3", "Tuner_4", "Playback_3", - "Reserved_1", - "Reserved_2", + "Backup_1", + "Backup_2", "Secondary_TV", }; @@ -101,21 +123,36 @@ final class HdmiUtils { * @return true if the given address is valid */ static boolean isValidAddress(int address) { - return (Constants.ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE); + return (ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE); + } + + static boolean isEligibleAddressForDevice(int deviceType, int logicalAddress) { + return isValidAddress(logicalAddress) + && ADDRESS_TO_TYPE.get(logicalAddress).contains(deviceType); + } + + static boolean isEligibleAddressForCecVersion(int cecVersion, int logicalAddress) { + if (isValidAddress(logicalAddress)) { + if (logicalAddress == ADDR_BACKUP_1 || logicalAddress == ADDR_BACKUP_2) { + return cecVersion == Constants.VERSION_2_0; + } + return true; + } + return false; } /** * Return the device type for the given logical address. * - * @param address logical address + * @param logicalAddress logical address * @return device type for the given logical address; DEVICE_INACTIVE * if the address is not valid. */ - static int getTypeFromAddress(int address) { - if (isValidAddress(address)) { - return ADDRESS_TO_TYPE[address]; + static List<Integer> getTypeFromAddress(int logicalAddress) { + if (isValidAddress(logicalAddress)) { + return ADDRESS_TO_TYPE.get(logicalAddress); } - return HdmiDeviceInfo.DEVICE_INACTIVE; + return Lists.newArrayList(HdmiDeviceInfo.DEVICE_INACTIVE); } /** @@ -142,10 +179,10 @@ final class HdmiUtils { * @throws IllegalArgumentException */ static void verifyAddressType(int logicalAddress, int deviceType) { - int actualDeviceType = getTypeFromAddress(logicalAddress); - if (actualDeviceType != deviceType) { + List<Integer> actualDeviceTypes = getTypeFromAddress(logicalAddress); + if (!actualDeviceTypes.contains(deviceType)) { throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType - + ", Actual:" + actualDeviceType); + + ", Actual:" + actualDeviceTypes); } } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index ece78bfa2769..a7f34ed85e0d 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -259,7 +259,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { } private void mayDisableSystemAudioAndARC(int address) { - if (HdmiUtils.getTypeFromAddress(address) != HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { + if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) { return; } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index edc7bd95a017..a307ea33abf7 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -183,8 +183,8 @@ final class NewDeviceAction extends HdmiCecFeatureAction { // Consume CEC messages we already got for this newly found device. tv().processDelayedMessages(mDeviceLogicalAddress); - if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) - == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { + if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + mDeviceLogicalAddress)) { tv().onNewAvrAdded(deviceInfo); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 047f1742f2ca..16a4b7250598 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1713,7 +1713,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSettings, context); mMenuController = new InputMethodMenuController(this); - mTracingThread = new HandlerThread("android.tracing", Process.THREAD_PRIORITY_BACKGROUND); + mTracingThread = new HandlerThread("android.tracing", Process.THREAD_PRIORITY_FOREGROUND); mTracingThread.start(); } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 9ca4d35f4579..9c48d23dade6 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -306,9 +306,8 @@ public class LocationManagerService extends ILocationManager.Stub { private void removeLocationProviderManager(LocationProviderManager manager) { synchronized (mProviderManagers) { - Preconditions.checkState(getLocationProviderManager(manager.getName()) == manager); - - mProviderManagers.remove(manager); + boolean removed = mProviderManagers.remove(manager); + Preconditions.checkArgument(removed); manager.setMockProvider(null); manager.setRealProvider(null); manager.stopManager(); diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index b4a172393ba6..f25c65192066 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -491,7 +491,7 @@ class LocationProviderManager extends builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS); } if (baseRequest.getMinUpdateIntervalMillis() < MIN_COARSE_INTERVAL_MS) { - builder.clearMinUpdateIntervalMillis(); + builder.setMinUpdateIntervalMillis(MIN_COARSE_INTERVAL_MS); } } @@ -1197,19 +1197,19 @@ class LocationProviderManager extends public void stopManager() { synchronized (mLock) { - mUserHelper.removeListener(mUserChangedListener); - mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); - - mStarted = false; - final long identity = Binder.clearCallingIdentity(); try { onEnabledChanged(UserHandle.USER_ALL); removeRegistrationIf(key -> true); - mEnabledListeners.clear(); } finally { Binder.restoreCallingIdentity(identity); } + + mUserHelper.removeListener(mUserChangedListener); + mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); + + Preconditions.checkState(mEnabledListeners.isEmpty()); + mStarted = false; } } @@ -1408,6 +1408,7 @@ class LocationProviderManager extends Location location; synchronized (mLock) { + Preconditions.checkState(mStarted); LastLocation lastLocation = mLastLocations.get(userId); if (lastLocation == null) { location = null; @@ -1429,6 +1430,7 @@ class LocationProviderManager extends public void injectLastLocation(Location location, int userId) { synchronized (mLock) { + Preconditions.checkState(mStarted); if (getLastLocationUnsafe(userId, PERMISSION_FINE, false, Long.MAX_VALUE) == null) { setLastLocation(location, userId); } @@ -1476,6 +1478,7 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { + Preconditions.checkState(mStarted); final long ident = Binder.clearCallingIdentity(); try { putRegistration(callback.asBinder(), registration); @@ -1517,6 +1520,7 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { + Preconditions.checkState(mStarted); final long ident = Binder.clearCallingIdentity(); try { putRegistration(listener.asBinder(), registration); @@ -1535,6 +1539,7 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { + Preconditions.checkState(mStarted); final long identity = Binder.clearCallingIdentity(); try { putRegistration(pendingIntent, registration); @@ -1546,6 +1551,7 @@ class LocationProviderManager extends public void unregisterLocationRequest(ILocationListener listener) { synchronized (mLock) { + Preconditions.checkState(mStarted); final long identity = Binder.clearCallingIdentity(); try { removeRegistration(listener.asBinder()); @@ -1557,6 +1563,7 @@ class LocationProviderManager extends public void unregisterLocationRequest(PendingIntent pendingIntent) { synchronized (mLock) { + Preconditions.checkState(mStarted); final long identity = Binder.clearCallingIdentity(); try { removeRegistration(pendingIntent); diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 71e7c8adc5db..12c24d418611 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -552,7 +552,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}. final PendingIntent pollIntent = - PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0); + PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), + PendingIntent.FLAG_IMMUTABLE); final long currentRealtime = SystemClock.elapsedRealtime(); mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 4040f4189698..ea7779a4dc8f 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -110,6 +110,7 @@ abstract public class ManagedServices { static final String ATT_IS_PRIMARY = "primary"; static final String ATT_VERSION = "version"; static final String ATT_DEFAULTS = "defaults"; + static final String ATT_USER_SET = "user_set_services"; static final int DB_VERSION = 3; @@ -150,7 +151,12 @@ abstract public class ManagedServices { // List of approved packages or components (by user, then by primary/secondary) that are // allowed to be bound as managed services. A package or component appearing in this list does // not mean that we are currently bound to said package/component. - private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); + protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); + + // List of packages or components (by user) that are configured to be enabled/disabled + // explicitly by the user + @GuardedBy("mApproved") + protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>(); // True if approved services are stored in xml, not settings. private boolean mUseXml; @@ -333,6 +339,12 @@ abstract public class ManagedServices { } } } + + pw.println(" Has user set:"); + Set<Integer> userIds = mUserSetServices.keySet(); + for (int userId : userIds) { + pw.println(" userId=" + userId + " value=" + mUserSetServices.get(userId)); + } } pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size() @@ -465,12 +477,20 @@ abstract public class ManagedServices { for (int j = 0; j < M; j++) { final boolean isPrimary = approvedByType.keyAt(j); final Set<String> approved = approvedByType.valueAt(j); - if (approved != null) { - String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved); + final Set<String> userSet = mUserSetServices.get(approvedUserId); + if (approved != null || userSet != null) { + String allowedItems = approved == null + ? "" + : String.join(ENABLED_SERVICES_SEPARATOR, approved); out.startTag(null, TAG_MANAGED_SERVICES); out.attribute(null, ATT_APPROVED_LIST, allowedItems); out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); + if (userSet != null) { + String userSetItems = + String.join(ENABLED_SERVICES_SEPARATOR, userSet); + out.attribute(null, ATT_USER_SET, userSetItems); + } writeExtraAttributes(out, approvedUserId); out.endTag(null, TAG_MANAGED_SERVICES); @@ -559,11 +579,12 @@ abstract public class ManagedServices { ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); final boolean isPrimary = XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true); + final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET); readExtraAttributes(tag, parser, resolvedUserId); if (allowedManagedServicePackages == null || allowedManagedServicePackages.test( getPackageName(approved), resolvedUserId, getRequiredPermission())) { if (mUm.getUserInfo(resolvedUserId) != null) { - addApprovedList(approved, resolvedUserId, isPrimary); + addApprovedList(approved, resolvedUserId, isPrimary, userSet); } mUseXml = true; } @@ -632,9 +653,16 @@ abstract public class ManagedServices { } protected void addApprovedList(String approved, int userId, boolean isPrimary) { + addApprovedList(approved, userId, isPrimary, approved); + } + + protected void addApprovedList(String approved, int userId, boolean isPrimary, String userSet) { if (TextUtils.isEmpty(approved)) { approved = ""; } + if (userSet == null) { + userSet = approved; + } synchronized (mApproved) { ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); if (approvedByType == null) { @@ -655,6 +683,19 @@ abstract public class ManagedServices { approvedList.add(approvedItem); } } + + ArraySet<String> userSetList = mUserSetServices.get(userId); + if (userSetList == null) { + userSetList = new ArraySet<>(); + mUserSetServices.put(userId, userSetList); + } + String[] userSetArray = userSet.split(ENABLED_SERVICES_SEPARATOR); + for (String pkgOrComponent : userSetArray) { + String approvedItem = getApprovedValue(pkgOrComponent); + if (approvedItem != null) { + userSetList.add(approvedItem); + } + } } } @@ -664,8 +705,14 @@ abstract public class ManagedServices { protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, boolean isPrimary, boolean enabled) { + setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, true); + } + + protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, + boolean isPrimary, boolean enabled, boolean userSet) { Slog.i(TAG, - (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent); + (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + + pkgOrComponent + " (userSet: " + userSet + ")"); synchronized (mApproved) { ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId); if (allowedByType == null) { @@ -686,6 +733,16 @@ abstract public class ManagedServices { approved.remove(approvedItem); } } + ArraySet<String> userSetServices = mUserSetServices.get(userId); + if (userSetServices == null) { + userSetServices = new ArraySet<>(); + mUserSetServices.put(userId, userSetServices); + } + if (userSet) { + userSetServices.add(pkgOrComponent); + } else { + userSetServices.remove(pkgOrComponent); + } } rebindServices(false, userId); @@ -761,6 +818,13 @@ abstract public class ManagedServices { return false; } + boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) { + synchronized (mApproved) { + ArraySet<String> services = mUserSetServices.get(userId); + return services != null && services.contains(pkgOrComponent); + } + } + protected boolean isPackageAllowed(String pkg, int userId) { if (pkg == null) { return false; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8ea432634e61..4f4f3c64b1b4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -97,6 +97,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.internal.util.CollectionUtils.emptyIfNull; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; @@ -304,6 +305,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; @@ -714,7 +716,7 @@ public class NotificationManagerService extends SystemService { try { getBinderService().setNotificationListenerAccessGrantedForUser(cn, - userId, true); + userId, true, true); } catch (RemoteException e) { e.printStackTrace(); } @@ -4822,9 +4824,9 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationListenerAccessGranted(ComponentName listener, - boolean granted) throws RemoteException { + boolean granted, boolean userSet) throws RemoteException { setNotificationListenerAccessGrantedForUser( - listener, getCallingUserHandle().getIdentifier(), granted); + listener, getCallingUserHandle().getIdentifier(), granted, userSet); } @Override @@ -4836,17 +4838,21 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId, - boolean granted) { + boolean granted, boolean userSet) { Objects.requireNonNull(listener); checkNotificationListenerAccess(); + if (!userSet && isNotificationListenerAccessUserSet(listener)) { + // Don't override user's choice + return; + } final long identity = Binder.clearCallingIdentity(); try { if (mAllowedManagedServicePackages.test( listener.getPackageName(), userId, mListeners.getRequiredPermission())) { mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(), - userId, false, granted); + userId, false, granted, userSet); mListeners.setPackageOrComponentEnabled(listener.flattenToString(), - userId, true, granted); + userId, true, granted, userSet); getContext().sendBroadcastAsUser(new Intent( ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) @@ -4861,6 +4867,11 @@ public class NotificationManagerService extends SystemService { } } + private boolean isNotificationListenerAccessUserSet(ComponentName listener) { + return mListeners.isPackageOrComponentUserSet(listener.flattenToString(), + getCallingUserHandle().getIdentifier()); + } + @Override public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant, int userId, boolean granted) { @@ -8830,14 +8841,12 @@ public class NotificationManagerService extends SystemService { public class NotificationAssistants extends ManagedServices { static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants"; - private static final String ATT_USER_SET = "user_set"; private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments"; private static final String ATT_TYPES = "types"; private final Object mLock = new Object(); @GuardedBy("mLock") - private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>(); private Set<String> mAllowedAdjustments = new ArraySet<>(); @Override @@ -9026,22 +9035,33 @@ public class NotificationManagerService extends SystemService { boolean hasUserSet(int userId) { synchronized (mLock) { - return mUserSetMap.getOrDefault(userId, false); + ArraySet<String> userSetServices = mUserSetServices.get(userId); + if (userSetServices == null) { + // Legacy case - no data means user-set, unless no assistant is set + return !mApproved.isEmpty(); + } + Map<Boolean, ArraySet<String>> approvedByType = emptyIfNull(mApproved.get(userId)); + return userSetServices.containsAll(emptyIfNull(approvedByType.get(true))) + && userSetServices.containsAll(emptyIfNull(approvedByType.get(false))); } } void setUserSet(int userId, boolean set) { synchronized (mLock) { - mUserSetMap.put(userId, set); + ArraySet<String> userSetServices = new ArraySet<>(); + if (set) { + ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); + if (approvedByType != null) { + for (int i = 0; i < approvedByType.size(); i++) { + userSetServices.addAll(approvedByType.valueAt(i)); + } + } + } + mUserSetServices.put(userId, userSetServices); } } @Override - protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException { - out.attribute(null, ATT_USER_SET, Boolean.toString(hasUserSet(userId))); - } - - @Override protected void readExtraAttributes(String tag, XmlPullParser parser, int userId) throws IOException { boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false); @@ -9279,18 +9299,6 @@ public class NotificationManagerService extends SystemService { super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); } - @Override - public void dump(PrintWriter pw, DumpFilter filter) { - super.dump(pw, filter); - pw.println(" Has user set:"); - synchronized (mLock) { - Set<Integer> userIds = mUserSetMap.keySet(); - for (int userId : userIds) { - pw.println(" userId=" + userId + " value=" + mUserSetMap.get(userId)); - } - } - } - private boolean isVerboseLogEnabled() { return Log.isLoggable("notification_assistant", Log.VERBOSE); } @@ -9307,8 +9315,8 @@ public class NotificationManagerService extends SystemService { @Override protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, - boolean isPrimary, boolean enabled) { - super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); + boolean isPrimary, boolean enabled, boolean userSet) { + super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet); getContext().sendBroadcastAsUser( new Intent(ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED) diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index 73272a012671..78c60d559ea9 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -214,7 +214,8 @@ public class NotificationShellCmd extends ShellCommand { if (peekNextArg() != null) { userId = Integer.parseInt(getNextArgRequired()); } - mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, true); + mBinderService.setNotificationListenerAccessGrantedForUser( + cn, userId, true, true); } break; case "disallow_listener": { @@ -227,7 +228,8 @@ public class NotificationShellCmd extends ShellCommand { if (peekNextArg() != null) { userId = Integer.parseInt(getNextArgRequired()); } - mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, false); + mBinderService.setNotificationListenerAccessGrantedForUser( + cn, userId, false, true); } break; case "allow_assistant": { diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java index 8f05636eed9c..f5d648910d90 100644 --- a/services/core/java/com/android/server/notification/ZenLog.java +++ b/services/core/java/com/android/server/notification/ZenLog.java @@ -39,7 +39,7 @@ public class ZenLog { // the ZenLog is *very* verbose, so be careful about setting this to true private static final boolean DEBUG = false; - private static final int SIZE = Build.IS_DEBUGGABLE ? 100 : 20; + private static final int SIZE = Build.IS_DEBUGGABLE ? 200 : 100; private static final long[] TIMES = new long[SIZE]; private static final int[] TYPES = new int[SIZE]; @@ -136,9 +136,14 @@ public class ZenLog { public static void traceConfig(String reason, ZenModeConfig oldConfig, ZenModeConfig newConfig) { - append(TYPE_CONFIG, reason - + "," + (newConfig != null ? newConfig.toString() : null) - + "," + ZenModeConfig.diff(oldConfig, newConfig)); + ZenModeConfig.Diff diff = ZenModeConfig.diff(oldConfig, newConfig); + if (diff.isEmpty()) { + append(TYPE_CONFIG, reason + " no changes"); + } else { + append(TYPE_CONFIG, reason + + ",\n" + (newConfig != null ? newConfig.toString() : null) + + ",\n" + ZenModeConfig.diff(oldConfig, newConfig)); + } } public static void traceDisableEffects(NotificationRecord record, String reason) { diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 571f79915484..50b4d438984c 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -108,7 +108,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { @Override public void onServiceAdded(ComponentName component) { if (DEBUG) Log.d(TAG, "onServiceAdded " + component); - mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded"); + mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded:" + component); } @Override diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 7992fea4a675..a12932a88487 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -1018,29 +1018,17 @@ public abstract class ApexManager { // the /apex directory is just a symlink to /system/apex. List<ActiveApexInfo> result = new ArrayList<>(); File apexDir = Environment.getApexDirectory(); - // In flattened configuration, init special-case the art directory and bind-mounts - // com.android.art.{release|debug} to com.android.art. At the time of writing, these - // directories are copied from the kArtApexDirNames variable in - // system/core/init/mount_namespace.cpp. - String[] skipDirs = {"com.android.art.release", "com.android.art.debug"}; if (apexDir.isDirectory()) { File[] files = apexDir.listFiles(); // listFiles might be null if system server doesn't have permission to read // a directory. if (files != null) { for (File file : files) { - if (file.isDirectory() && !file.getName().contains("@")) { - boolean skip = false; - for (String skipDir : skipDirs) { - if (file.getName().equals(skipDir)) { - skip = true; - break; - } - } - if (!skip) { - result.add( - new ActiveApexInfo(file, Environment.getRootDirectory())); - } + if (file.isDirectory() && !file.getName().contains("@") + // In flattened configuration, init special-cases the art directory + // and bind-mounts com.android.art.debug to com.android.art. + && !file.getName().equals("com.android.art.debug")) { + result.add(new ActiveApexInfo(file, Environment.getRootDirectory())); } } } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index c0329b2b2fe6..efbdfeaaeb93 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -918,7 +918,7 @@ class InstantAppRegistry { try { for (String grantedPermission : appInfo.getGrantedPermissions()) { final boolean propagatePermission = - mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission); + mPermissionManager.canPropagatePermissionToInstantApp(grantedPermission); if (propagatePermission && pkg.getRequestedPermissions().contains( grantedPermission)) { mService.grantRuntimePermission(pkg.getPackageName(), grantedPermission, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7eb4fd968c36..4fdb9fbb3b3f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -375,7 +375,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.permission.BasePermission; +import com.android.server.pm.permission.Permission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal; @@ -2742,7 +2742,6 @@ public class PackageManagerService extends IPackageManager.Stub new UserDataPreparer(installer, installLock, context, onlyCore), lock), (i, pm) -> new Settings(Environment.getDataDirectory(), - i.getPermissionManagerServiceInternal().getPermissionSettings(), RuntimePermissionsPersistence.createInstance(), i.getPermissionManagerServiceInternal(), lock), (i, pm) -> AppsFilter.create(pm.mPmInternal, i), @@ -3173,6 +3172,8 @@ public class PackageManagerService extends IPackageManager.Stub /* excludePreCreated= */ false)); t.traceEnd(); + mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions); + // Clean up orphaned packages for which the code path doesn't exist // and they are an update to a system app - caused by bug/32321269 final int packageSettingCount = mSettings.mPackages.size(); @@ -3597,7 +3598,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); - mPermissionManager.readStateFromPackageSettingsTEMP(); + mPermissionManager.readLegacyPermissionStateTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -11433,7 +11434,7 @@ public class PackageManagerService extends IPackageManager.Stub if (verifyPackageUpdateLPr(orig, pkg)) { Slog.i(TAG, "Adopting permissions from " + origName + " to " + pkg.getPackageName()); - mSettings.mPermissions.transferPermissions(origName, pkg.getPackageName()); + mPermissionManager.transferPermissions(origName, pkg.getPackageName()); } } } @@ -12305,9 +12306,8 @@ public class PackageManagerService extends IPackageManager.Stub // user-installed version of the application will be ignored. if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) { if (mExpectingBetter.containsKey(pkg.getPackageName())) { - logCriticalInfo(Log.WARN, - "Relax SCAN_REQUIRE_KNOWN requirement for package " - + pkg.getPackageName()); + Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package " + + pkg.getPackageName()); } else { PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName()); if (known != null) { @@ -13326,13 +13326,21 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) { final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils - .enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled", callingUid); + final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID + || callingUid == Process.SYSTEM_UID; + if (!calledFromSystemOrPhone) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + "setSystemAppHiddenUntilInstalled"); + } + synchronized (mLock) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null || !pkgSetting.isSystem()) { return; } + if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) { + throw new SecurityException("Only system or phone callers can modify core apps"); + } pkgSetting.getPkgState().setHiddenUntilInstalled(hidden); final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName); if (disabledPs == null) { @@ -13345,14 +13353,22 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) { final int callingUid = Binder.getCallingUid(); - PackageManagerServiceUtils - .enforceSystemOrPhoneCaller("setSystemAppInstallState", callingUid); + final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID + || callingUid == Process.SYSTEM_UID; + if (!calledFromSystemOrPhone) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + "setSystemAppHiddenUntilInstalled"); + } + synchronized (mLock) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); // The target app should always be in system if (pkgSetting == null || !pkgSetting.isSystem()) { return false; } + if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) { + throw new SecurityException("Only system or phone callers can modify core apps"); + } // Check if the install state is the same if (pkgSetting.getInstalled(userId) == installed) { return false; @@ -17928,7 +17944,7 @@ public class PackageManagerService extends IPackageManager.Stub final int N = ArrayUtils.size(parsedPackage.getPermissions()); for (int i = N - 1; i >= 0; i--) { final ParsedPermission perm = parsedPackage.getPermissions().get(i); - final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName()); + final Permission bp = mPermissionManager.getPermissionTEMP(perm.getName()); // Don't allow anyone but the system to define ephemeral permissions. if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0 @@ -24216,9 +24232,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writeLegacyPermissionStateTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); - mPermissionManager.readStateFromPackageSettingsTEMP(); + mPermissionManager.readLegacyPermissionStateTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -26361,13 +26377,14 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * Temporary method that wraps mSettings.writeLPr() and calls - * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand. + * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's + * writeLegacyPermissionsTEMP() and writeLegacyPermissionStateTEMP() beforehand. * * TODO(zhanghai): This should be removed once we finish migration of permission storage. */ private void writeSettingsLPrTEMP() { - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions); + mPermissionManager.writeLegacyPermissionStateTEMP(); mSettings.writeLPr(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index e5dad8570254..b05fd47383fd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -103,7 +103,8 @@ import java.util.zip.GZIPInputStream; * {@hide} */ public class PackageManagerServiceUtils { - private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; + private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; + private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG = pkgSetting -> pkgSetting.pkg == null; @@ -349,7 +350,12 @@ public class PackageManagerServiceUtils { } public static void dumpCriticalInfo(ProtoOutputStream proto) { - try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { + final File file = getSettingsProblemFile(); + final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE; + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + if (skipSize > 0) { + in.skip(skipSize); + } String line = null; while ((line = in.readLine()) != null) { if (line.contains("ignored: updated version")) continue; @@ -360,7 +366,12 @@ public class PackageManagerServiceUtils { } public static void dumpCriticalInfo(PrintWriter pw, String msg) { - try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { + final File file = getSettingsProblemFile(); + final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE; + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + if (skipSize > 0) { + in.skip(skipSize); + } String line = null; while ((line = in.readLine()) != null) { if (line.contains("ignored: updated version")) continue; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c7304c2695c9..cd9a4e72672f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -107,11 +107,10 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.LegacyPermissionDataProvider; +import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.permission.LegacyPermissionState.PermissionState; -import com.android.server.pm.permission.PermissionSettings; import com.android.server.utils.TimingsTraceAndSlog; import libcore.io.IoUtils; @@ -420,7 +419,7 @@ public final class Settings { public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages); /** Settings and other information about permissions */ - final PermissionSettings mPermissions; + final LegacyPermissionSettings mPermissions; private final LegacyPermissionDataProvider mPermissionDataProvider; @@ -440,11 +439,10 @@ public final class Settings { mKernelMappingFilename = null; } - Settings(File dataDir, PermissionSettings permissionSettings, - RuntimePermissionsPersistence runtimePermissionsPersistence, + Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, LegacyPermissionDataProvider permissionDataProvider, Object lock) { mLock = lock; - mPermissions = permissionSettings; + mPermissions = new LegacyPermissionSettings(lock); mRuntimePermissionsPersistence = new RuntimePermissionPersistence( runtimePermissionsPersistence); mPermissionDataProvider = permissionDataProvider; @@ -489,10 +487,6 @@ public final class Settings { mRenamedPackages.remove(pkgName); } - public boolean canPropagatePermissionToInstantApp(String permName) { - return mPermissions.canPropagatePermissionToInstantApp(permName); - } - /** Gets and optionally creates a new shared user id. */ SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags, boolean create) throws PackageManagerException { @@ -2128,23 +2122,14 @@ public final class Settings { String tagName = parser.getName(); if (tagName.equals(TAG_ITEM)) { String name = parser.getAttributeValue(null, ATTR_NAME); - - BasePermission bp = mPermissions.getPermission(name); - if (bp == null) { - Slog.w(PackageManagerService.TAG, "Unknown permission: " + name); - XmlUtils.skipCurrentTag(parser); - continue; - } - String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); final boolean granted = grantedStr == null || Boolean.parseBoolean(grantedStr); - String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); final int flags = (flagsStr != null) ? Integer.parseInt(flagsStr, 16) : 0; - - permissionsState.putInstallPermissionState(new PermissionState(bp, granted, flags)); + permissionsState.putInstallPermissionState(new PermissionState(name, granted, + flags)); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: " + parser.getName()); @@ -2867,10 +2852,6 @@ public final class Settings { } } - void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws IOException { - bp.writeLPr(serializer); - } - boolean readLPw(@NonNull List<UserInfo> users) { FileInputStream str = null; if (mBackupSettingsFilename.exists()) { @@ -4813,13 +4794,7 @@ public final class Settings { && !permissionNames.contains(perm)) { continue; } - pw.print(prefix); pw.print(" "); pw.print(perm); - final BasePermission bp = mPermissions.getPermission(perm); - if (bp != null && bp.isHardOrSoftRestricted()) { - pw.println(": restricted=true"); - } else { - pw.println(); - } + pw.print(prefix); pw.print(" "); pw.println(perm); } } @@ -5024,7 +4999,9 @@ public final class Settings { void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, DumpState dumpState) { - mPermissions.dumpPermissions(pw, packageName, permissionNames, + LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames, + mPermissionDataProvider.getLegacyPermissions(), + mPermissionDataProvider.getAllAppOpPermissionPackages(), (mReadExternalStorageEnforced == Boolean.TRUE), dumpState); } @@ -5544,18 +5521,11 @@ public final class Settings { int permissionsSize = permissions.size(); for (int i = 0; i < permissionsSize; i++) { RuntimePermissionsState.PermissionState permission = permissions.get(i); - String name = permission.getName(); - BasePermission basePermission = mPermissions.getPermission(name); - if (basePermission == null) { - Slog.w(PackageManagerService.TAG, "Unknown permission:" + name); - continue; - } boolean granted = permission.isGranted(); int flags = permission.getFlags(); - - permissionsState.putRuntimePermissionState(new PermissionState(basePermission, - granted, flags), userId); + permissionsState.putRuntimePermissionState(new PermissionState(name, granted, + flags), userId); } } @@ -5650,23 +5620,14 @@ public final class Settings { switch (parser.getName()) { case TAG_ITEM: { String name = parser.getAttributeValue(null, ATTR_NAME); - BasePermission bp = mPermissions.getPermission(name); - if (bp == null) { - Slog.w(PackageManagerService.TAG, "Unknown permission:" + name); - XmlUtils.skipCurrentTag(parser); - continue; - } - String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); final boolean granted = grantedStr == null || Boolean.parseBoolean(grantedStr); - String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); final int flags = (flagsStr != null) ? Integer.parseInt(flagsStr, 16) : 0; - - permissionsState.putRuntimePermissionState(new PermissionState(bp, granted, - flags), userId); + permissionsState.putRuntimePermissionState(new PermissionState(name, + granted, flags), userId); } break; } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 3bad3cb1d372..e99e301e6da1 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -485,7 +485,8 @@ public class StagingManager { } } - private void resumeSession(@NonNull PackageInstallerSession session) { + private void resumeSession(@NonNull PackageInstallerSession session) + throws PackageManagerException { Slog.d(TAG, "Resuming session " + session.sessionId); final boolean hasApex = session.containsApexSession(); @@ -550,10 +551,8 @@ public class StagingManager { if (apexSessionInfo == null) { final String errorMsg = "apexd did not know anything about a staged session " + "supposed to be activated"; - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - errorMsg); - abortCheckpoint(session.sessionId, errorMsg); - return; + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); } if (isApexSessionFailed(apexSessionInfo)) { String errorMsg = "APEX activation failed. Check logcat messages from apexd " @@ -562,10 +561,8 @@ public class StagingManager { errorMsg = "Session reverted due to crashing native process: " + mNativeFailureReason; } - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - errorMsg); - abortCheckpoint(session.sessionId, errorMsg); - return; + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); } if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) { // Apexd did not apply the session for some unknown reason. There is no @@ -573,43 +570,22 @@ public class StagingManager { // it as failed. final String errorMsg = "Staged session " + session.sessionId + "at boot " + "didn't activate nor fail. Marking it as failed anyway."; - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - errorMsg); - abortCheckpoint(session.sessionId, errorMsg); - return; + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); } } - // Handle apk and apk-in-apex installation - try { - if (hasApex) { - checkInstallationOfApkInApexSuccessful(session); - checkDuplicateApkInApex(session); - snapshotAndRestoreForApexSession(session); - Slog.i(TAG, "APEX packages in session " + session.sessionId - + " were successfully activated. Proceeding with APK packages, if any"); - } - // The APEX part of the session is activated, proceed with the installation of APKs. - Slog.d(TAG, "Installing APK packages in session " + session.sessionId); - installApksInSession(session); - } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - abortCheckpoint(session.sessionId, e.getMessage()); - - // If checkpoint is not supported, we have to handle failure for one staged session. - if (!hasApex) { - return; - } - if (!mApexManager.revertActiveSessions()) { - Slog.e(TAG, "Failed to abort APEXd session"); - } else { - Slog.e(TAG, - "Successfully aborted apexd session. Rebooting device in order to revert " - + "to the previous state of APEXd."); - mPowerManager.reboot(null); - } - return; + // Handle apk and apk-in-apex installation + if (hasApex) { + checkInstallationOfApkInApexSuccessful(session); + checkDuplicateApkInApex(session); + snapshotAndRestoreForApexSession(session); + Slog.i(TAG, "APEX packages in session " + session.sessionId + + " were successfully activated. Proceeding with APK packages, if any"); } + // The APEX part of the session is activated, proceed with the installation of APKs. + Slog.d(TAG, "Installing APK packages in session " + session.sessionId); + installApksInSession(session); Slog.d(TAG, "Marking session " + session.sessionId + " as applied"); session.setStagedSessionApplied(); @@ -633,6 +609,25 @@ public class StagingManager { } } + void onInstallationFailure(PackageInstallerSession session, PackageManagerException e) { + session.setStagedSessionFailed(e.error, e.getMessage()); + abortCheckpoint(session.sessionId, e.getMessage()); + + // If checkpoint is not supported, we have to handle failure for one staged session. + if (!session.containsApexSession()) { + return; + } + + if (!mApexManager.revertActiveSessions()) { + Slog.e(TAG, "Failed to abort APEXd session"); + } else { + Slog.e(TAG, + "Successfully aborted apexd session. Rebooting device in order to revert " + + "to the previous state of APEXd."); + mPowerManager.reboot(null); + } + } + private String getReasonForRevert() { if (!TextUtils.isEmpty(mFailureReason)) { return mFailureReason; @@ -933,7 +928,16 @@ public class StagingManager { } else { // Session had already being marked ready. Start the checks to verify if there is any // follow-up work. - resumeSession(session); + try { + resumeSession(session); + } catch (PackageManagerException e) { + onInstallationFailure(session, e); + } catch (Exception e) { + final String errorMsg = "Staged install failed due to unhandled exception"; + Slog.e(TAG, errorMsg, e); + onInstallationFailure(session, new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg)); + } } } @@ -1059,19 +1063,26 @@ public class StagingManager { onPreRebootVerificationComplete(session); return; } - switch (msg.what) { - case MSG_PRE_REBOOT_VERIFICATION_START: - handlePreRebootVerification_Start(session); - break; - case MSG_PRE_REBOOT_VERIFICATION_APEX: - handlePreRebootVerification_Apex(session, rollbackId); - break; - case MSG_PRE_REBOOT_VERIFICATION_APK: - handlePreRebootVerification_Apk(session); - break; - case MSG_PRE_REBOOT_VERIFICATION_END: - handlePreRebootVerification_End(session); - break; + try { + switch (msg.what) { + case MSG_PRE_REBOOT_VERIFICATION_START: + handlePreRebootVerification_Start(session); + break; + case MSG_PRE_REBOOT_VERIFICATION_APEX: + handlePreRebootVerification_Apex(session, rollbackId); + break; + case MSG_PRE_REBOOT_VERIFICATION_APK: + handlePreRebootVerification_Apk(session); + break; + case MSG_PRE_REBOOT_VERIFICATION_END: + handlePreRebootVerification_End(session); + break; + } + } catch (Exception e) { + Slog.e(TAG, "Pre-reboot verification failed due to unhandled exception", e); + onPreRebootVerificationFailure(session, + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "Pre-reboot verification failed due to unhandled exception: " + e); } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index a0344e27f96c..7f29cd94bfca 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -4458,7 +4458,7 @@ public class UserManagerService extends IUserManager.Stub { } } if (userInfo == null) { - throw new SecurityException("userId can only be the calling user or a managed " + throw new SecurityException("userId can only be the calling user or a " + "profile associated with this user"); } return userInfo.creationTime; diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index f74913c03ce2..fadd0c850f18 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -727,10 +727,14 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { "compiled_trace.pb"); try { boolean exists = Files.exists(tracePath); - Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist")); + if (DEBUG) { + Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist")); + } if (exists) { long bytes = Files.size(tracePath); - Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes)); + if (DEBUG) { + Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes)); + } return bytes > 0L; } return exists; diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java new file mode 100644 index 000000000000..a19a05ae021c --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PermissionInfo; +import android.util.Log; + +import com.android.server.pm.DumpState; +import com.android.server.pm.PackageManagerService; + +import libcore.util.EmptyArray; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Legacy permission definition. + */ +//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public final class LegacyPermission { + /** + * The permission is defined in a manifest. + */ + public static final int TYPE_MANIFEST = 0; + + /** + * The permission is defined in a system config. + */ + public static final int TYPE_CONFIG = 1; + + /** + * The permission is defined dynamically. + */ + public static final int TYPE_DYNAMIC = 2; + + /** + * @hide + */ + @IntDef({ + TYPE_MANIFEST, + TYPE_CONFIG, + TYPE_DYNAMIC, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionType {} + + private static final String ATTR_NAME = "name"; + private static final String ATTR_PACKAGE = "package"; + private static final String TAG_ITEM = "item"; + + @NonNull + private final PermissionInfo mPermissionInfo; + @PermissionType + private final int mType; + private final int mUid; + @NonNull + private final int[] mGids; + + /** + * Create a new instance of this class. + * + * @param permissionInfo the {@link PermissionInfo} for the permission + * @param type the type of the permission + * @param uid the UID defining the permission + * @param gids the GIDs associated with the permission + */ + public LegacyPermission(@NonNull PermissionInfo permissionInfo, @PermissionType int type, + int uid, @NonNull int[] gids) { + mPermissionInfo = permissionInfo; + mType = type; + mUid = uid; + mGids = gids; + } + + private LegacyPermission(@NonNull String name, @NonNull String packageName, + @PermissionType int type) { + mPermissionInfo = new PermissionInfo(); + mPermissionInfo.name = name; + mPermissionInfo.packageName = packageName; + // Default to most conservative protection level. + mPermissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; + mType = type; + mUid = 0; + mGids = EmptyArray.INT; + } + + /** + * Get the {@link PermissionInfo} for this mission. + * + * @return the {@link PermissionInfo} + */ + @NonNull + public PermissionInfo getPermissionInfo() { + return mPermissionInfo; + } + + /** + * Get the type of this mission. + * + * @return the type + */ + public int getType() { + return mType; + } + + /** + * @hide + */ + public static boolean read(@NonNull Map<String, LegacyPermission> out, + @NonNull XmlPullParser parser) { + final String tagName = parser.getName(); + if (!tagName.equals(TAG_ITEM)) { + return false; + } + final String name = parser.getAttributeValue(null, ATTR_NAME); + final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + final String ptype = parser.getAttributeValue(null, "type"); + if (name == null || packageName == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: permissions has" + " no name at " + + parser.getPositionDescription()); + return false; + } + final boolean dynamic = "dynamic".equals(ptype); + LegacyPermission bp = out.get(name); + // If the permission is builtin, do not clobber it. + if (bp == null || bp.mType != TYPE_CONFIG) { + bp = new LegacyPermission(name.intern(), packageName, + dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST); + } + bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection", + PermissionInfo.PROTECTION_NORMAL); + bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel( + bp.mPermissionInfo.protectionLevel); + if (dynamic) { + bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0); + bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label"); + } + out.put(bp.mPermissionInfo.name, bp); + return true; + } + + private static int readInt(@NonNull XmlPullParser parser, @Nullable String namespace, + @NonNull String name, int defaultValue) { + final String value = parser.getAttributeValue(namespace, name); + if (value == null) { + return defaultValue; + } + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: attribute " + name + + " has bad integer value " + value + " at " + + parser.getPositionDescription()); + return defaultValue; + } + } + + /** + * @hide + */ + public void write(@NonNull XmlSerializer serializer) throws IOException { + if (mPermissionInfo.packageName == null) { + return; + } + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, mPermissionInfo.name); + serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName); + if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", + Integer.toString(mPermissionInfo.protectionLevel)); + } + if (mType == TYPE_DYNAMIC) { + serializer.attribute(null, "type", "dynamic"); + if (mPermissionInfo.icon != 0) { + serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon)); + } + if (mPermissionInfo.nonLocalizedLabel != null) { + serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString()); + } + } + serializer.endTag(null, TAG_ITEM); + } + + /** + * @hide + */ + public boolean dump(@NonNull PrintWriter pw, @NonNull String packageName, + @NonNull Set<String> permissionNames, boolean readEnforced, boolean printedSomething, + @NonNull DumpState dumpState) { + if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) { + return false; + } + if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) { + return false; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Permissions:"); + } + pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(this))); + pw.println("):"); + pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName); + pw.print(" uid="); pw.print(mUid); + pw.print(" gids="); pw.print(Arrays.toString(mGids)); + pw.print(" type="); pw.print(mType); + pw.print(" prot="); + pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel)); + if (mPermissionInfo != null) { + pw.print(" perm="); pw.println(mPermissionInfo); + if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0 + || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) { + pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags)); + } + } + if (Objects.equals(mPermissionInfo.name, + android.Manifest.permission.READ_EXTERNAL_STORAGE)) { + pw.print(" enforced="); + pw.println(readEnforced); + } + return true; + } +} diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java index 346a2c527fcb..0e790b1899ed 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java @@ -19,12 +19,32 @@ package com.android.server.pm.permission; import android.annotation.AppIdInt; import android.annotation.NonNull; +import java.util.List; +import java.util.Map; +import java.util.Set; + /** * An interface for legacy code to read permission data in order to maintain compatibility. */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public interface LegacyPermissionDataProvider { /** + * Get all the legacy permissions currently registered in the system. + * + * @return the legacy permissions + */ + @NonNull + List<LegacyPermission> getLegacyPermissions(); + + /** + * Get all the package names requesting app op permissions. + * + * @return a map of app op permission names to package names requesting them + */ + @NonNull + Map<String, Set<String>> getAllAppOpPermissionPackages(); + + /** * Get the legacy permission state of an app ID, either a package or a shared user. * * @param appId the app ID diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java new file mode 100644 index 000000000000..cc0b79872ba0 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.XmlUtils; +import com.android.server.pm.DumpState; +import com.android.server.pm.PackageManagerService; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Legacy permission settings for migration. + */ +public class LegacyPermissionSettings { + /** + * All of the permissions known to the system. The mapping is from permission + * name to permission object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, LegacyPermission> mPermissions = new ArrayMap<>(); + + /** + * All permission trees known to the system. The mapping is from permission tree + * name to permission object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, LegacyPermission> mPermissionTrees = new ArrayMap<>(); + + @NonNull + private final Object mLock; + + public LegacyPermissionSettings(@NonNull Object lock) { + mLock = lock; + } + + @NonNull + public List<LegacyPermission> getPermissions() { + synchronized (mLock) { + return new ArrayList<>(mPermissions.values()); + } + } + + @NonNull + public List<LegacyPermission> getPermissionTrees() { + synchronized (mLock) { + return new ArrayList<>(mPermissionTrees.values()); + } + } + + public void replacePermissions(@NonNull List<LegacyPermission> permissions) { + synchronized (mLock) { + mPermissions.clear(); + final int permissionsSize = permissions.size(); + for (int i = 0; i < permissionsSize; i++) { + final LegacyPermission permission = permissions.get(i); + mPermissions.put(permission.getPermissionInfo().name, permission); + } + } + } + + public void replacePermissionTrees(@NonNull List<LegacyPermission> permissionTrees) { + synchronized (mLock) { + mPermissionTrees.clear(); + final int permissionsSize = permissionTrees.size(); + for (int i = 0; i < permissionsSize; i++) { + final LegacyPermission permissionTree = permissionTrees.get(i); + mPermissionTrees.put(permissionTree.getPermissionInfo().name, permissionTree); + } + } + } + + public void readPermissions(@NonNull XmlPullParser parser) throws IOException, + XmlPullParserException { + synchronized (mLock) { + readPermissions(mPermissions, parser); + } + } + + public void readPermissionTrees(@NonNull XmlPullParser parser) throws IOException, + XmlPullParserException { + synchronized (mLock) { + readPermissions(mPermissionTrees, parser); + } + } + + public static void readPermissions(@NonNull ArrayMap<String, LegacyPermission> out, + @NonNull XmlPullParser parser) throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (!LegacyPermission.read(out, parser)) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element reading permissions: " + parser.getName() + " at " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + public void writePermissions(@NonNull XmlSerializer serializer) throws IOException { + synchronized (mLock) { + for (LegacyPermission bp : mPermissions.values()) { + bp.write(serializer); + } + } + } + + public void writePermissionTrees(@NonNull XmlSerializer serializer) throws IOException { + synchronized (mLock) { + for (LegacyPermission bp : mPermissionTrees.values()) { + bp.write(serializer); + } + } + } + + public static void dumpPermissions(@NonNull PrintWriter pw, @Nullable String packageName, + @Nullable ArraySet<String> permissionNames, @NonNull List<LegacyPermission> permissions, + @NonNull Map<String, Set<String>> appOpPermissionPackages, + boolean externalStorageEnforced, @NonNull DumpState dumpState) { + boolean printedSomething = false; + final int permissionsSize = permissions.size(); + for (int i = 0; i < permissionsSize; i++) { + final LegacyPermission permission = permissions.get(i); + printedSomething = permission.dump(pw, packageName, permissionNames, + externalStorageEnforced, printedSomething, dumpState); + } + if (packageName == null && permissionNames == null) { + boolean firstEntry = true; + for (final Map.Entry<String, Set<String>> entry : appOpPermissionPackages.entrySet()) { + if (firstEntry) { + firstEntry = false; + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("AppOp Permissions:"); + } + pw.print(" AppOp Permission "); + pw.print(entry.getKey()); + pw.println(":"); + for (final String appOpPackageName : entry.getValue()) { + pw.print(" "); + pw.println(appOpPackageName); + } + } + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java index 63f69cede59c..0e60367c243b 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java @@ -252,7 +252,7 @@ public final class LegacyPermissionState { */ public static final class PermissionState { @NonNull - private final BasePermission mPermission; + private final String mName; private final boolean mGranted; @@ -261,40 +261,30 @@ public final class LegacyPermissionState { /** * Create a new instance of this class. * - * @param permission the {@link BasePermission} for the permission + * @param name the name of the permission * @param granted whether the permission is granted * @param flags the permission flags */ - public PermissionState(@NonNull BasePermission permission, boolean granted, int flags) { - mPermission = permission; + public PermissionState(@NonNull String name, boolean granted, int flags) { + mName = name; mGranted = granted; mFlags = flags; } private PermissionState(@NonNull PermissionState other) { - mPermission = other.mPermission; + mName = other.mName; mGranted = other.mGranted; mFlags = other.mFlags; } /** - * Get the {@link BasePermission} for the permission. - * - * @return the {@link BasePermission} - */ - @NonNull - public BasePermission getPermission() { - return mPermission; - } - - /** * Get the permission name. * * @return the permission name */ @NonNull public String getName() { - return mPermission.getName(); + return mName; } /** diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 155d71673e06..c121e6b4a763 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -19,6 +19,7 @@ package com.android.server.pm.permission; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; @@ -28,31 +29,25 @@ import android.os.UserHandle; import android.util.Log; import android.util.Slog; -import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; import libcore.util.EmptyArray; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Collection; -import java.util.Map; import java.util.Objects; -import java.util.Set; -public final class BasePermission { - private static final String TAG = "PackageManager"; +/** + * Permission definition. + */ +public final class Permission { + private static final String TAG = "Permission"; - public static final int TYPE_MANIFEST = 0; - public static final int TYPE_CONFIG = 1; - public static final int TYPE_DYNAMIC = 2; + public static final int TYPE_MANIFEST = LegacyPermission.TYPE_MANIFEST; + public static final int TYPE_CONFIG = LegacyPermission.TYPE_CONFIG; + public static final int TYPE_DYNAMIC = LegacyPermission.TYPE_DYNAMIC; @IntDef({ TYPE_MANIFEST, TYPE_CONFIG, @@ -70,18 +65,13 @@ public final class BasePermission { @Retention(RetentionPolicy.SOURCE) public @interface ProtectionLevel {} - private static final String ATTR_NAME = "name"; - private static final String ATTR_PACKAGE = "package"; - private static final String TAG_ITEM = "item"; - - private boolean mPermissionDefinitionChanged; - @NonNull private PermissionInfo mPermissionInfo; private boolean mReconciled; - private final @PermissionType int mType; + @PermissionType + private final int mType; /** UID that owns the definition of this permission */ private int mUid; @@ -96,7 +86,10 @@ public final class BasePermission { */ private boolean mGidsPerUser; - public BasePermission(@NonNull String name, String packageName, @PermissionType int type) { + private boolean mDefinitionChanged; + + public Permission(@NonNull String name, @NonNull String packageName, + @PermissionType int type) { mPermissionInfo = new PermissionInfo(); mPermissionInfo.name = name; mPermissionInfo.packageName = packageName; @@ -105,10 +98,27 @@ public final class BasePermission { mType = type; } - @Override - public String toString() { - return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " - + mPermissionInfo.name + "}"; + public Permission(@NonNull PermissionInfo permissionInfo, @PermissionType int type) { + mPermissionInfo = permissionInfo; + mType = type; + } + + @NonNull + public PermissionInfo getPermissionInfo() { + return mPermissionInfo; + } + + public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) { + if (permissionInfo != null) { + mPermissionInfo = permissionInfo; + } else { + final PermissionInfo newPermissionInfo = new PermissionInfo(); + newPermissionInfo.name = mPermissionInfo.name; + newPermissionInfo.packageName = mPermissionInfo.packageName; + newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel; + mPermissionInfo = newPermissionInfo; + } + mReconciled = permissionInfo != null; } @NonNull @@ -120,14 +130,11 @@ public final class BasePermission { return mPermissionInfo.protectionLevel; } + @NonNull public String getPackageName() { return mPermissionInfo.packageName; } - public boolean isPermissionDefinitionChanged() { - return mPermissionDefinitionChanged; - } - public int getType() { return mType; } @@ -136,34 +143,26 @@ public final class BasePermission { return mUid; } - public void setGids(@NonNull int[] gids, boolean gidsPerUser) { - mGids = gids; - mGidsPerUser = gidsPerUser; + public boolean hasGids() { + return mGids.length != 0; } - public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) { - if (permissionInfo != null) { - mPermissionInfo = permissionInfo; - } else { - final PermissionInfo newPermissionInfo = new PermissionInfo(); - newPermissionInfo.name = mPermissionInfo.name; - newPermissionInfo.packageName = mPermissionInfo.packageName; - newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel; - mPermissionInfo = newPermissionInfo; - } - mReconciled = permissionInfo != null; + @NonNull + public int[] getRawGids() { + return mGids; } - public void setPermissionDefinitionChanged(boolean shouldOverride) { - mPermissionDefinitionChanged = shouldOverride; + public boolean areGidsPerUser() { + return mGidsPerUser; } - public boolean hasGids() { - return mGids.length != 0; + public void setGids(@NonNull int[] gids, boolean gidsPerUser) { + mGids = gids; + mGidsPerUser = gidsPerUser; } @NonNull - public int[] computeGids(int userId) { + public int[] computeGids(@UserIdInt int userId) { if (mGidsPerUser) { final int[] userGids = new int[mGids.length]; for (int i = 0; i < mGids.length; i++) { @@ -176,19 +175,28 @@ public final class BasePermission { } } - public int calculateFootprint(BasePermission perm) { - if (mUid == perm.mUid) { - return perm.mPermissionInfo.name.length() + perm.mPermissionInfo.calculateFootprint(); + public boolean isDefinitionChanged() { + return mDefinitionChanged; + } + + public void setDefinitionChanged(boolean definitionChanged) { + mDefinitionChanged = definitionChanged; + } + + public int calculateFootprint(@NonNull Permission permission) { + if (mUid == permission.mUid) { + return permission.mPermissionInfo.name.length() + + permission.mPermissionInfo.calculateFootprint(); } return 0; } - public boolean isPermission(ParsedPermission perm) { + public boolean isPermission(@NonNull ParsedPermission parsedPermission) { if (mPermissionInfo == null) { return false; } - return Objects.equals(mPermissionInfo.packageName, perm.getPackageName()) - && Objects.equals(mPermissionInfo.name, perm.getName()); + return Objects.equals(mPermissionInfo.packageName, parsedPermission.getPackageName()) + && Objects.equals(mPermissionInfo.name, parsedPermission.getName()); } public boolean isDynamic() { @@ -323,8 +331,8 @@ public final class BasePermission { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; } - public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { - if (!origPackageName.equals(mPermissionInfo.packageName)) { + public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { + if (!oldPackageName.equals(mPermissionInfo.packageName)) { return; } final PermissionInfo newPermissionInfo = new PermissionInfo(); @@ -339,28 +347,29 @@ public final class BasePermission { } public boolean addToTree(@ProtectionLevel int protectionLevel, - @NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) { + @NonNull PermissionInfo permissionInfo, @NonNull Permission permissionTree) { final boolean changed = (mPermissionInfo.protectionLevel != protectionLevel || !mReconciled - || mUid != tree.mUid + || mUid != permissionTree.mUid || !Objects.equals(mPermissionInfo.packageName, - tree.mPermissionInfo.packageName) + permissionTree.mPermissionInfo.packageName) || !comparePermissionInfos(mPermissionInfo, permissionInfo)); mPermissionInfo = new PermissionInfo(permissionInfo); - mPermissionInfo.packageName = tree.mPermissionInfo.packageName; + mPermissionInfo.packageName = permissionTree.mPermissionInfo.packageName; mPermissionInfo.protectionLevel = protectionLevel; mReconciled = true; - mUid = tree.mUid; + mUid = permissionTree.mUid; return changed; } - public void updateDynamicPermission(Collection<BasePermission> permissionTrees) { - if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" - + getName() + " pkg=" + getPackageName() - + " info=" + mPermissionInfo); + public void updateDynamicPermission(@NonNull Collection<Permission> permissionTrees) { + if (PackageManagerService.DEBUG_SETTINGS) { + Log.v(TAG, "Dynamic permission: name=" + getName() + " pkg=" + getPackageName() + + " info=" + mPermissionInfo); + } if (mType == TYPE_DYNAMIC) { - final BasePermission tree = findPermissionTree(permissionTrees, mPermissionInfo.name); + final Permission tree = findPermissionTree(permissionTrees, mPermissionInfo.name); if (tree != null) { mPermissionInfo.packageName = tree.mPermissionInfo.packageName; mReconciled = true; @@ -369,19 +378,21 @@ public final class BasePermission { } } - static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal, - @Nullable BasePermission bp, @NonNull PermissionInfo p, - @NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees, + @NonNull + static Permission createOrUpdate(PackageManagerInternal packageManagerInternal, + @Nullable Permission permission, @NonNull PermissionInfo permissionInfo, + @NonNull AndroidPackage pkg, @NonNull Collection<Permission> permissionTrees, boolean chatty) { // Allow system apps to redefine non-system permissions boolean ownerChanged = false; - if (bp != null && !Objects.equals(bp.mPermissionInfo.packageName, p.packageName)) { + if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName, + permissionInfo.packageName)) { final boolean currentOwnerIsSystem; - if (!bp.mReconciled) { + if (!permission.mReconciled) { currentOwnerIsSystem = false; } else { AndroidPackage currentPackage = packageManagerInternal.getPackage( - bp.mPermissionInfo.packageName); + permission.mPermissionInfo.packageName); if (currentPackage == null) { currentOwnerIsSystem = false; } else { @@ -390,54 +401,56 @@ public final class BasePermission { } if (pkg.isSystem()) { - if (bp.mType == BasePermission.TYPE_CONFIG && !bp.mReconciled) { + if (permission.mType == Permission.TYPE_CONFIG && !permission.mReconciled) { // It's a built-in permission and no owner, take ownership now - p.flags |= PermissionInfo.FLAG_INSTALLED; - bp.mPermissionInfo = p; - bp.mReconciled = true; - bp.mUid = pkg.getUid(); + permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED; + permission.mPermissionInfo = permissionInfo; + permission.mReconciled = true; + permission.mUid = pkg.getUid(); } else if (!currentOwnerIsSystem) { String msg = "New decl " + pkg + " of permission " - + p.name + " is system; overriding " + bp.mPermissionInfo.packageName; + + permissionInfo.name + " is system; overriding " + + permission.mPermissionInfo.packageName; PackageManagerService.reportSettingsProblem(Log.WARN, msg); ownerChanged = true; - bp = null; + permission = null; } } } - if (bp == null) { - bp = new BasePermission(p.name, p.packageName, TYPE_MANIFEST); + if (permission == null) { + permission = new Permission(permissionInfo.name, permissionInfo.packageName, + TYPE_MANIFEST); } - boolean wasNormal = bp.isNormal(); + boolean wasNormal = permission.isNormal(); StringBuilder r = null; - if (!bp.mReconciled) { - if (bp.mPermissionInfo.packageName == null - || bp.mPermissionInfo.packageName.equals(p.packageName)) { - final BasePermission tree = findPermissionTree(permissionTrees, p.name); + if (!permission.mReconciled) { + if (permission.mPermissionInfo.packageName == null + || permission.mPermissionInfo.packageName.equals(permissionInfo.packageName)) { + final Permission tree = findPermissionTree(permissionTrees, permissionInfo.name); if (tree == null - || tree.mPermissionInfo.packageName.equals(p.packageName)) { - p.flags |= PermissionInfo.FLAG_INSTALLED; - bp.mPermissionInfo = p; - bp.mReconciled = true; - bp.mUid = pkg.getUid(); + || tree.mPermissionInfo.packageName.equals(permissionInfo.packageName)) { + permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED; + permission.mPermissionInfo = permissionInfo; + permission.mReconciled = true; + permission.mUid = pkg.getUid(); if (chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } - r.append(p.name); + r.append(permissionInfo.name); } } else { - Slog.w(TAG, "Permission " + p.name + " from package " - + p.packageName + " ignored: base tree " + Slog.w(TAG, "Permission " + permissionInfo.name + " from package " + + permissionInfo.packageName + " ignored: base tree " + tree.mPermissionInfo.name + " is from package " + tree.mPermissionInfo.packageName); } } else { - Slog.w(TAG, "Permission " + p.name + " from package " - + p.packageName + " ignored: original from " - + bp.mPermissionInfo.packageName); + Slog.w(TAG, "Permission " + permissionInfo.name + " from package " + + permissionInfo.packageName + " ignored: original from " + + permission.mPermissionInfo.packageName); } } else if (chatty) { if (r == null) { @@ -446,42 +459,47 @@ public final class BasePermission { r.append(' '); } r.append("DUP:"); - r.append(p.name); + r.append(permissionInfo.name); } - if (bp.isRuntime() && (ownerChanged || wasNormal)) { + if (permission.isRuntime() && (ownerChanged || wasNormal)) { // If this is a runtime permission and the owner has changed, or this was a normal // permission, then permission state should be cleaned up - bp.mPermissionDefinitionChanged = true; + permission.mDefinitionChanged = true; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { Log.d(TAG, " Permissions: " + r); } - return bp; + return permission; } - static BasePermission enforcePermissionTree( - Collection<BasePermission> permissionTrees, String permName, int callingUid) { - if (permName != null) { - BasePermission bp = findPermissionTree(permissionTrees, permName); - if (bp != null) { - if (bp.mUid == UserHandle.getAppId(callingUid)) { - return bp; + @NonNull + public static Permission enforcePermissionTree(@NonNull Collection<Permission> permissionTrees, + @NonNull String permissionName, int callingUid) { + if (permissionName != null) { + final Permission permissionTree = Permission.findPermissionTree(permissionTrees, + permissionName); + if (permissionTree != null) { + if (permissionTree.getUid() == UserHandle.getAppId(callingUid)) { + return permissionTree; } throw new SecurityException("Calling uid " + callingUid + " is not allowed to add to permission tree " - + bp.mPermissionInfo.name + " owned by uid " + bp.mUid); + + permissionTree.getName() + " owned by uid " + + permissionTree.getUid()); } } - throw new SecurityException("No permission tree found for " + permName); + throw new SecurityException("No permission tree found for " + permissionName); } - private static BasePermission findPermissionTree( - Collection<BasePermission> permissionTrees, String permName) { - for (BasePermission bp : permissionTrees) { - if (permName.startsWith(bp.mPermissionInfo.name) - && permName.length() > bp.mPermissionInfo.name.length() - && permName.charAt(bp.mPermissionInfo.name.length()) == '.') { - return bp; + @Nullable + private static Permission findPermissionTree(@NonNull Collection<Permission> permissionTrees, + @NonNull String permissionName) { + for (final Permission permissionTree : permissionTrees) { + final String permissionTreeName = permissionTree.getName(); + if (permissionName.startsWith(permissionTreeName) + && permissionName.length() > permissionTreeName.length() + && permissionName.charAt(permissionTreeName.length()) == '.') { + return permissionTree; } } return null; @@ -512,7 +530,7 @@ public final class BasePermission { @NonNull public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) { - PermissionInfo permissionInfo; + final PermissionInfo permissionInfo; if (mPermissionInfo != null) { permissionInfo = new PermissionInfo(mPermissionInfo); if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) { @@ -539,79 +557,6 @@ public final class BasePermission { return permissionInfo; } - public static boolean readLPw(@NonNull Map<String, BasePermission> out, - @NonNull XmlPullParser parser) { - final String tagName = parser.getName(); - if (!tagName.equals(TAG_ITEM)) { - return false; - } - final String name = parser.getAttributeValue(null, ATTR_NAME); - final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); - final String ptype = parser.getAttributeValue(null, "type"); - if (name == null || packageName == null) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: permissions has" + " no name at " - + parser.getPositionDescription()); - return false; - } - final boolean dynamic = "dynamic".equals(ptype); - BasePermission bp = out.get(name); - // If the permission is builtin, do not clobber it. - if (bp == null || bp.mType != TYPE_CONFIG) { - bp = new BasePermission(name.intern(), packageName, - dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST); - } - bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection", - PermissionInfo.PROTECTION_NORMAL); - bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel( - bp.mPermissionInfo.protectionLevel); - if (dynamic) { - bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0); - bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label"); - } - out.put(bp.mPermissionInfo.name, bp); - return true; - } - - private static int readInt(XmlPullParser parser, String ns, String name, int defValue) { - String v = parser.getAttributeValue(ns, name); - try { - if (v == null) { - return defValue; - } - return Integer.parseInt(v); - } catch (NumberFormatException e) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: attribute " + name - + " has bad integer value " + v + " at " - + parser.getPositionDescription()); - } - return defValue; - } - - public void writeLPr(@NonNull XmlSerializer serializer) throws IOException { - if (mPermissionInfo.packageName == null) { - return; - } - serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, mPermissionInfo.name); - serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName); - if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) { - serializer.attribute(null, "protection", - Integer.toString(mPermissionInfo.protectionLevel)); - } - if (mType == TYPE_DYNAMIC) { - serializer.attribute(null, "type", "dynamic"); - if (mPermissionInfo.icon != 0) { - serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon)); - } - if (mPermissionInfo.nonLocalizedLabel != null) { - serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString()); - } - } - serializer.endTag(null, TAG_ITEM); - } - private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { if (pi1.icon != pi2.icon) return false; if (pi1.logo != pi2.logo) return false; @@ -627,42 +572,4 @@ public final class BasePermission { //if (pi1.descriptionRes != pi2.descriptionRes) return false; return true; } - - public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName, - @NonNull Set<String> permissionNames, boolean readEnforced, - boolean printedSomething, @NonNull DumpState dumpState) { - if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) { - return false; - } - if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) { - return false; - } - if (!printedSomething) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Permissions:"); - } - pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(this))); - pw.println("):"); - pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName); - pw.print(" uid="); pw.print(mUid); - pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM))); - pw.print(" type="); pw.print(mType); - pw.print(" prot="); - pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel)); - if (mPermissionInfo != null) { - pw.print(" perm="); pw.println(mPermissionInfo); - if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0 - || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) { - pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags)); - } - } - if (Objects.equals(mPermissionInfo.name, - android.Manifest.permission.READ_EXTERNAL_STORAGE)) { - pw.print(" enforced="); - pw.println(readEnforced); - } - return true; - } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index da4ef63d6945..6d987aee8995 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -252,7 +252,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal storage for permissions and related settings */ @GuardedBy("mLock") - private final PermissionSettings mSettings; + private final PermissionRegistry mRegistry; /** Injector that can be used to facilitate testing. */ private final Injector mInjector; @@ -387,7 +387,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { mLock = externalLock; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); - mSettings = new PermissionSettings(mLock); + mRegistry = new PermissionRegistry(mLock); mAppOpsManager = context.getSystemService(AppOpsManager.class); mHandlerThread = new ServiceThread(TAG, @@ -409,10 +409,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { synchronized (mLock) { for (int i=0; i<permConfig.size(); i++) { final SystemConfig.PermissionEntry perm = permConfig.valueAt(i); - BasePermission bp = mSettings.getPermissionLocked(perm.name); + Permission bp = mRegistry.getPermissionLocked(perm.name); if (bp == null) { - bp = new BasePermission(perm.name, "android", BasePermission.TYPE_CONFIG); - mSettings.putPermissionLocked(perm.name, bp); + bp = new Permission(perm.name, "android", Permission.TYPE_CONFIG); + mRegistry.addPermissionLocked(bp); } if (perm.gids != null) { bp.setGids(perm.gids, perm.perUser); @@ -485,9 +485,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Nullable - BasePermission getPermission(String permName) { + Permission getPermission(String permName) { synchronized (mLock) { - return mSettings.getPermissionLocked(permName); + return mRegistry.getPermissionLocked(permName); } } @@ -501,7 +501,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return null; } synchronized (mLock) { - final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName); + final ArraySet<String> pkgs = mRegistry.getAppOpPermissionPackagesLocked(permName); if (pkgs == null) { return null; } @@ -518,9 +518,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { return ParceledListSlice.emptyList(); } synchronized (mLock) { - final int n = mSettings.mPermissionGroups.size(); - final ArrayList<PermissionGroupInfo> out = new ArrayList<>(n); - for (ParsedPermissionGroup pg : mSettings.mPermissionGroups.values()) { + final List<PermissionGroupInfo> out = new ArrayList<>(); + for (ParsedPermissionGroup pg : mRegistry.getPermissionGroupsLocked()) { out.add(PackageInfoUtils.generatePermissionGroupInfo(pg, flags)); } return new ParceledListSlice<>(out); @@ -538,7 +537,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } synchronized (mLock) { return PackageInfoUtils.generatePermissionGroupInfo( - mSettings.mPermissionGroups.get(groupName), flags); + mRegistry.getPermissionGroupLocked(groupName), flags); } } @@ -555,7 +554,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage, callingUid); synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); + final Permission bp = mRegistry.getPermissionLocked(permName); if (bp == null) { return null; } @@ -585,11 +584,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { return null; } synchronized (mLock) { - if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) { + if (groupName != null && mRegistry.getPermissionGroupLocked(groupName) == null) { return null; } final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); - for (BasePermission bp : mSettings.mPermissions.values()) { + for (Permission bp : mRegistry.getPermissionsLocked()) { if (Objects.equals(bp.getGroup(), groupName)) { out.add(bp.generatePermissionInfo(flags)); } @@ -607,24 +606,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (info.labelRes == 0 && info.nonLocalizedLabel == null) { throw new SecurityException("Label must be specified in permission"); } - final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid); + final Permission tree = mRegistry.enforcePermissionTree(info.name, callingUid); final boolean added; final boolean changed; synchronized (mLock) { - BasePermission bp = mSettings.getPermissionLocked(info.name); + Permission bp = mRegistry.getPermissionLocked(info.name); added = bp == null; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); if (added) { enforcePermissionCapLocked(info, tree); - bp = new BasePermission(info.name, tree.getPackageName(), - BasePermission.TYPE_DYNAMIC); + bp = new Permission(info.name, tree.getPackageName(), + Permission.TYPE_DYNAMIC); } else if (!bp.isDynamic()) { throw new SecurityException("Not allowed to modify non-dynamic permission " + info.name); } changed = bp.addToTree(fixedLevel, info, tree); if (added) { - mSettings.putPermissionLocked(info.name, bp); + mRegistry.addPermissionLocked(bp); } } if (changed) { @@ -639,9 +638,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } - final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid); + final Permission tree = mRegistry.enforcePermissionTree(permName, callingUid); synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); + final Permission bp = mRegistry.getPermissionLocked(permName); if (bp == null) { return; } @@ -650,7 +649,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + permName); } - mSettings.removePermissionLocked(permName); + mRegistry.removePermissionLocked(permName); mPackageManagerInt.writeSettings(false); } } @@ -683,7 +682,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } synchronized (mLock) { - if (mSettings.getPermissionLocked(permName) == null) { + if (mRegistry.getPermissionLocked(permName) == null) { return 0; } @@ -808,10 +807,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - final BasePermission bp; + final Permission bp; final boolean permissionUpdated; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permName); + bp = mRegistry.getPermissionLocked(permName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } @@ -970,7 +969,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (isInstantApp) { - return mSettings.isPermissionInstant(permissionName); + return mRegistry.isPermissionInstant(permissionName); } return true; @@ -1232,7 +1231,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permName) { synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); + final Permission bp = mRegistry.getPermissionLocked(permName); if (bp == null) { Slog.w(TAG, "No such permissions: " + permName); return false; @@ -1473,9 +1472,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - final BasePermission bp; + final Permission bp; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permName); + bp = mRegistry.getPermissionLocked(permName); } if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); @@ -1627,7 +1626,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } - final BasePermission bp = mSettings.getPermission(permName); + final Permission bp = mRegistry.getPermission(permName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } @@ -1808,9 +1807,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < permissionCount; i++) { final String permName = pkg.getRequestedPermissions().get(i); - final BasePermission bp; + final Permission bp; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permName); + bp = mRegistry.getPermissionLocked(permName); } if (bp == null) { continue; @@ -2093,7 +2092,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return false; } - BasePermission permission = getPermission(permName); + Permission permission = getPermission(permName); if (permission == null) { return false; } @@ -2348,7 +2347,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int permNum = 0; permNum < numPermissions; permNum++) { String permName = permissionsToRevoke.get(permNum); - BasePermission bp = mSettings.getPermission(permName); + Permission bp = mRegistry.getPermission(permName); if (bp == null || !bp.isRuntime()) { continue; } @@ -2388,7 +2387,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } } - bp.setPermissionDefinitionChanged(false); + bp.setDefinitionChanged(false); } } @@ -2407,7 +2406,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // permissions for one app being granted to someone just because they happen // to be in a group defined by another app (before this had no implications). if (pkg.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) { - p.setParsedPermissionGroup(mSettings.mPermissionGroups.get(p.getGroup())); + p.setParsedPermissionGroup(mRegistry.getPermissionGroupLocked(p.getGroup())); // Warn for a permission in an unknown group. if (DEBUG_PERMISSIONS && p.getGroup() != null && p.getParsedPermissionGroup() == null) { @@ -2418,24 +2417,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p, PackageManager.GET_META_DATA); - final BasePermission bp; + final Permission bp; if (p.isTree()) { - bp = BasePermission.createOrUpdate( + bp = Permission.createOrUpdate( mPackageManagerInt, - mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg, - mSettings.getAllPermissionTreesLocked(), chatty); - mSettings.putPermissionTreeLocked(p.getName(), bp); + mRegistry.getPermissionTreeLocked(p.getName()), permissionInfo, pkg, + mRegistry.getPermissionTreesLocked(), chatty); + mRegistry.addPermissionTreeLocked(bp); } else { - bp = BasePermission.createOrUpdate( + bp = Permission.createOrUpdate( mPackageManagerInt, - mSettings.getPermissionLocked(p.getName()), - permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty); - mSettings.putPermissionLocked(p.getName(), bp); + mRegistry.getPermissionLocked(p.getName()), + permissionInfo, pkg, mRegistry.getPermissionTreesLocked(), chatty); + mRegistry.addPermissionLocked(bp); } if (bp.isInstalled()) { p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); } - if (bp.isPermissionDefinitionChanged()) { + if (bp.isDefinitionChanged()) { definitionChangedPermissions.add(p.getName()); } } @@ -2444,45 +2443,46 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) { - final int N = ArrayUtils.size(pkg.getPermissionGroups()); - StringBuilder r = null; - for (int i=0; i<N; i++) { - final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i); - final ParsedPermissionGroup cur = mSettings.mPermissionGroups.get(pg.getName()); - final String curPackageName = (cur == null) ? null : cur.getPackageName(); - final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName); - if (cur == null || isPackageUpdate) { - mSettings.mPermissionGroups.put(pg.getName(), pg); - if (chatty && DEBUG_PACKAGE_SCANNING) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - if (isPackageUpdate) { - r.append("UPD:"); + synchronized (mLock) { + final int N = ArrayUtils.size(pkg.getPermissionGroups()); + StringBuilder r = null; + for (int i = 0; i < N; i++) { + final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i); + final ParsedPermissionGroup cur = mRegistry.getPermissionGroupLocked(pg.getName()); + final String curPackageName = (cur == null) ? null : cur.getPackageName(); + final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName); + if (cur == null || isPackageUpdate) { + mRegistry.addPermissionGroupLocked(pg); + if (chatty && DEBUG_PACKAGE_SCANNING) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + if (isPackageUpdate) { + r.append("UPD:"); + } + r.append(pg.getName()); } - r.append(pg.getName()); - } - } else { - Slog.w(TAG, "Permission group " + pg.getName() + " from package " - + pg.getPackageName() + " ignored: original from " - + cur.getPackageName()); - if (chatty && DEBUG_PACKAGE_SCANNING) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); + } else { + Slog.w(TAG, "Permission group " + pg.getName() + " from package " + + pg.getPackageName() + " ignored: original from " + + cur.getPackageName()); + if (chatty && DEBUG_PACKAGE_SCANNING) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append("DUP:"); + r.append(pg.getName()); } - r.append("DUP:"); - r.append(pg.getName()); } } + if (r != null && DEBUG_PACKAGE_SCANNING) { + Log.d(TAG, " Permission Groups: " + r); + } } - if (r != null && DEBUG_PACKAGE_SCANNING) { - Log.d(TAG, " Permission Groups: " + r); - } - } private void removeAllPermissions(AndroidPackage pkg, boolean chatty) { @@ -2491,9 +2491,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { StringBuilder r = null; for (int i=0; i<N; i++) { ParsedPermission p = pkg.getPermissions().get(i); - BasePermission bp = mSettings.mPermissions.get(p.getName()); + Permission bp = mRegistry.getPermissionLocked(p.getName()); if (bp == null) { - bp = mSettings.mPermissionTrees.get(p.getName()); + bp = mRegistry.getPermissionTreeLocked(p.getName()); } if (bp != null && bp.isPermission(p)) { bp.setPermissionInfo(null); @@ -2507,11 +2507,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } if (p.isAppOp()) { - ArraySet<String> appOpPkgs = - mSettings.mAppOpPermissionPackages.get(p.getName()); - if (appOpPkgs != null) { - appOpPkgs.remove(pkg.getPackageName()); - } + // TODO(zhanghai): Should we just remove the entry for this permission directly? + mRegistry.removeAppOpPermissionPackageLocked(p.getName(), pkg.getPackageName()); } } if (r != null) { @@ -2522,14 +2519,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { r = null; for (int i=0; i<N; i++) { String perm = pkg.getRequestedPermissions().get(i); - if (mSettings.isPermissionAppOp(perm)) { - ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm); - if (appOpPkgs != null) { - appOpPkgs.remove(pkg.getPackageName()); - if (appOpPkgs.isEmpty()) { - mSettings.mAppOpPermissionPackages.remove(perm); - } - } + if (mRegistry.isPermissionAppOp(perm)) { + mRegistry.removeAppOpPermissionPackageLocked(perm, pkg.getPackageName()); } } if (r != null) { @@ -2567,7 +2558,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions()); instantPermissions.removeIf(permissionName -> { - BasePermission permission = mSettings.getPermission(permissionName); + Permission permission = mRegistry.getPermission(permissionName); if (permission == null) { return true; } @@ -2585,7 +2576,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { - BasePermission permission = mSettings.getPermission(permissionName); + Permission permission = mRegistry.getPermission(permissionName); if (permission == null) { return EmptyArray.INT; } @@ -2638,7 +2629,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < requestedPermissionsSize; i++) { final String permissionName = pkg.getRequestedPermissions().get(i); - final BasePermission permission = mSettings.getPermission(permissionName); + final Permission permission = mRegistry.getPermission(permissionName); if (permission == null) { continue; } @@ -2683,7 +2674,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } for (String permissionName : uidRequestedPermissions) { - BasePermission permission = mSettings.getPermission(permissionName); + Permission permission = mRegistry.getPermission(permissionName); if (permission == null) { continue; } @@ -2741,7 +2732,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < requestedPermissionsSize; i++) { final String permName = requestedPermissions.get(i); - final BasePermission bp = mSettings.getPermission(permName); + final Permission bp = mRegistry.getPermission(permName); final boolean appSupportsRuntimePermissions = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; String legacyActivityRecognitionPermission = null; @@ -2834,7 +2825,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Keep track of app op permissions. if (bp.isAppOp()) { - mSettings.addAppOpPackage(perm, pkg.getPackageName()); + mRegistry.addAppOpPermissionPackageLocked(perm, pkg.getPackageName()); } boolean shouldGrantNormalPermission = true; @@ -3057,7 +3048,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (String permission : ps.getGrantedPermissions()) { if (!pkg.getImplicitPermissions().contains(permission)) { - BasePermission bp = mSettings.getPermissionLocked(permission); + Permission bp = mRegistry.getPermissionLocked(permission); if (bp != null && bp.isRuntime()) { int flags = ps.getPermissionFlags(permission); @@ -3131,11 +3122,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { + " for " + pkgName); } - ps.grantPermission(mSettings.getPermissionLocked(newPerm)); + ps.grantPermission(mRegistry.getPermissionLocked(newPerm)); } // Add permission flags - ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags); + ps.updatePermissionFlags(mRegistry.getPermission(newPerm), flags, flags); } /** @@ -3206,7 +3197,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm); if (sourcePerms != null) { - BasePermission bp = mSettings.getPermissionLocked(newPerm); + Permission bp = mRegistry.getPermissionLocked(newPerm); if (bp.isRuntime()) { if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { @@ -3221,7 +3212,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); sourcePermNum++) { final String sourcePerm = sourcePerms.valueAt(sourcePermNum); - BasePermission sourceBp = mSettings.getPermissionLocked(sourcePerm); + Permission sourceBp = mRegistry.getPermissionLocked(sourcePerm); if (!sourceBp.isRuntime()) { inheritsFromInstallPerm = true; break; @@ -3344,7 +3335,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg, - @NonNull PackageSetting pkgSetting, @NonNull BasePermission bp) { + @NonNull PackageSetting pkgSetting, @NonNull Permission bp) { // expect single system package String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames( PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM)); @@ -3510,7 +3501,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private PackageParser.SigningDetails getSourcePackageSigningDetails( - @NonNull BasePermission bp) { + @NonNull Permission bp) { final PackageSetting ps = getSourcePackageSetting(bp); if (ps == null) { return PackageParser.SigningDetails.UNKNOWN; @@ -3519,13 +3510,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Nullable - private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) { + private PackageSetting getSourcePackageSetting(@NonNull Permission bp) { final String sourcePackageName = bp.getPackageName(); return mPackageManagerInt.getPackageSetting(sourcePackageName); } private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg, - boolean isUpdatedSystemApp, @NonNull BasePermission permission) { + boolean isUpdatedSystemApp, @NonNull Permission permission) { if (!pkg.isPrivileged()) { return false; } @@ -3671,9 +3662,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.getPackageName(), userId); for (String permission : pkg.getRequestedPermissions()) { - final BasePermission bp; + final Permission bp; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permission); + bp = mRegistry.getPermissionLocked(permission); } if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && (!instantApp || bp.isInstant()) @@ -3713,7 +3704,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < permissionCount; j++) { final String permissionName = pkg.getRequestedPermissions().get(j); - final BasePermission bp = mSettings.getPermissionLocked(permissionName); + final Permission bp = mRegistry.getPermissionLocked(permissionName); if (bp == null || !bp.isHardOrSoftRestricted()) { continue; @@ -3874,7 +3865,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { int affectedUserId = UserHandle.USER_NULL; // Update permissions for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { - BasePermission bp = mSettings.getPermission(eachPerm); + Permission bp = mRegistry.getPermission(eachPerm); if (bp == null) { continue; } @@ -3948,7 +3939,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int requestedPermCount = pkg.getRequestedPermissions().size(); for (int j = 0; j < requestedPermCount; j++) { String permission = pkg.getRequestedPermissions().get(j); - BasePermission bp = mSettings.getPermissionLocked(permission); + Permission bp = mRegistry.getPermissionLocked(permission); if (bp != null) { usedPermissions.add(permission); } @@ -3963,7 +3954,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = permissionStatesSize - 1; i >= 0; i--) { PermissionState permissionState = permissionStates.get(i); if (!usedPermissions.contains(permissionState.getName())) { - BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); + Permission bp = mRegistry.getPermissionLocked(permissionState.getName()); if (bp != null) { if (uidState.removePermissionState(bp.getName()) && permissionState.isRuntime()) { @@ -4036,7 +4027,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Cache background -> foreground permission mapping. // Only system declares background permissions, hence mapping does never change. mBackgroundPermissions = new ArrayMap<>(); - for (BasePermission bp : mSettings.getAllPermissionsLocked()) { + for (Permission bp : mRegistry.getPermissionsLocked()) { if (bp.getBackgroundPermission() != null) { String fgPerm = bp.getName(); String bgPerm = bp.getBackgroundPermission(); @@ -4180,13 +4171,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } boolean changed = false; - Set<BasePermission> needsUpdate = null; + Set<Permission> needsUpdate = null; synchronized (mLock) { - final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator(); - while (it.hasNext()) { - final BasePermission bp = it.next(); + for (final Permission bp : mRegistry.getPermissionsLocked()) { if (bp.isDynamic()) { - bp.updateDynamicPermission(mSettings.mPermissionTrees.values()); + bp.updateDynamicPermission(mRegistry.getPermissionTreesLocked()); } if (!packageName.equals(bp.getPackageName())) { // Not checking sourcePackageSetting because it can be null when @@ -4198,13 +4187,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Set to changed for either install or uninstall changed = true; if (needsUpdate == null) { - needsUpdate = new ArraySet<>(mSettings.mPermissions.size()); + needsUpdate = new ArraySet<>(); } needsUpdate.add(bp); } } if (needsUpdate != null) { - for (final BasePermission bp : needsUpdate) { + for (final Permission bp : needsUpdate) { // If the target package is being uninstalled, we need to revoke this permission // From all other packages if (pkg == null || !hasPermission(pkg, bp.getName())) { @@ -4236,7 +4225,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } }); } - mSettings.removePermissionLocked(bp.getName()); + mRegistry.removePermissionLocked(bp.getName()); continue; } final AndroidPackage sourcePkg = @@ -4250,7 +4239,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } Slog.w(TAG, "Removing dangling permission: " + bp.getName() + " from package " + bp.getPackageName()); - mSettings.removePermissionLocked(bp.getName()); + mRegistry.removePermissionLocked(bp.getName()); } } } @@ -4316,11 +4305,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } boolean changed = false; - Set<BasePermission> needsUpdate = null; + Set<Permission> needsUpdate = null; synchronized (mLock) { - final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); + final Iterator<Permission> it = mRegistry.getPermissionTreesLocked().iterator(); while (it.hasNext()) { - final BasePermission bp = it.next(); + final Permission bp = it.next(); if (!packageName.equals(bp.getPackageName())) { // Not checking sourcePackageSetting because it can be null when // the permission source package is the target package and the target package is @@ -4336,13 +4325,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { it.remove(); } if (needsUpdate == null) { - needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size()); + needsUpdate = new ArraySet<>(); } needsUpdate.add(bp); } } if (needsUpdate != null) { - for (final BasePermission bp : needsUpdate) { + for (final Permission bp : needsUpdate) { final AndroidPackage sourcePkg = mPackageManagerInt.getPackage(bp.getPackageName()); final PackageSetting sourcePs = @@ -4354,7 +4343,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() + " from package " + bp.getPackageName()); - mSettings.removePermissionLocked(bp.getName()); + mRegistry.removePermissionLocked(bp.getName()); } } } @@ -4536,16 +4525,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @GuardedBy({"mSettings.mLock", "mLock"}) - private int calculateCurrentPermissionFootprintLocked(BasePermission tree) { + private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) { int size = 0; - for (BasePermission perm : mSettings.mPermissions.values()) { - size += tree.calculateFootprint(perm); + for (final Permission permission : mRegistry.getPermissionsLocked()) { + size += permissionTree.calculateFootprint(permission); } return size; } @GuardedBy({"mSettings.mLock", "mLock"}) - private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) { + private void enforcePermissionCapLocked(PermissionInfo info, Permission tree) { // We calculate the max size of permissions defined by this uid and throw // if that plus the size of 'info' would exceed our stated maximum. if (tree.getUid() != Process.SYSTEM_UID) { @@ -4670,7 +4659,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private void readStateFromPackageSettings() { + private void readLegacyPermissionState() { final int[] userIds = getAllUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { final int appId = ps.getAppId(); @@ -4684,24 +4673,30 @@ public class PermissionManagerService extends IPermissionManager.Stub { final UidPermissionState uidState = userState.getOrCreateUidState(appId); uidState.reset(); uidState.setMissing(legacyState.isMissing(userId)); - readStateFromPermissionStates(uidState, + readLegacyPermissionStatesLocked(uidState, legacyState.getInstallPermissionStates()); - readStateFromPermissionStates(uidState, + readLegacyPermissionStatesLocked(uidState, legacyState.getRuntimePermissionStates(userId)); } } }); } - private void readStateFromPermissionStates(@NonNull UidPermissionState uidState, + private void readLegacyPermissionStatesLocked(@NonNull UidPermissionState uidState, @NonNull Collection<LegacyPermissionState.PermissionState> permissionStates) { for (final LegacyPermissionState.PermissionState permissionState : permissionStates) { - uidState.putPermissionState(permissionState.getPermission(), - permissionState.isGranted(), permissionState.getFlags()); + final String permissionName = permissionState.getName(); + final Permission permission = mRegistry.getPermissionLocked(permissionName); + if (permission == null) { + Slog.w(TAG, "Unknown permission: " + permissionName); + continue; + } + uidState.putPermissionState(permission, permissionState.isGranted(), + permissionState.getFlags()); } } - private void writeStateToPackageSettings() { + private void writeLegacyPermissionState() { final int[] userIds; synchronized (mLock) { userIds = mState.getUserIds(); @@ -4738,12 +4733,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionState permissionState = permissionStates.get(i); final LegacyPermissionState.PermissionState legacyPermissionState = - new LegacyPermissionState.PermissionState( - permissionState.getPermission(), + new LegacyPermissionState.PermissionState(permissionState.getName(), permissionState.isGranted(), permissionState.getFlags()); if (permissionState.isRuntime()) { - legacyState.putRuntimePermissionState(legacyPermissionState, - userId); + legacyState.putRuntimePermissionState(legacyPermissionState, userId); } else { legacyState.putInstallPermissionState(legacyPermissionState); } @@ -4753,6 +4746,108 @@ public class PermissionManagerService extends IPermissionManager.Stub { }); } + private void readLegacyPermissions(@NonNull LegacyPermissionSettings legacyPermissionSettings) { + for (int readPermissionOrPermissionTree = 0; readPermissionOrPermissionTree < 2; + readPermissionOrPermissionTree++) { + final List<LegacyPermission> legacyPermissions = readPermissionOrPermissionTree == 0 + ? legacyPermissionSettings.getPermissions() + : legacyPermissionSettings.getPermissionTrees(); + synchronized (mLock) { + final int legacyPermissionsSize = legacyPermissions.size(); + for (int i = 0; i < legacyPermissionsSize; i++) { + final LegacyPermission legacyPermission = legacyPermissions.get(i); + final Permission permission = new Permission( + legacyPermission.getPermissionInfo(), legacyPermission.getType()); + if (readPermissionOrPermissionTree == 0) { + // Config permissions are currently read in PermissionManagerService + // constructor. The old behavior was to add other attributes to the config + // permission in LegacyPermission.read(), so equivalently we can add the + // GIDs to the new permissions here, since config permissions created in + // PermissionManagerService constructor get only their names and GIDs there. + final Permission configPermission = mRegistry.getPermission( + permission.getName()); + if (configPermission != null + && configPermission.getType() == Permission.TYPE_CONFIG) { + permission.setGids(configPermission.getRawGids(), + configPermission.areGidsPerUser()); + } + mRegistry.addPermissionLocked(permission); + } else { + mRegistry.addPermissionTreeLocked(permission); + } + } + } + } + } + + private void writeLegacyPermissions( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + for (int writePermissionOrPermissionTree = 0; writePermissionOrPermissionTree < 2; + writePermissionOrPermissionTree++) { + final List<LegacyPermission> legacyPermissions = new ArrayList<>(); + synchronized (mLock) { + final Collection<Permission> permissions = writePermissionOrPermissionTree == 0 + ? mRegistry.getPermissionsLocked() : mRegistry.getPermissionTreesLocked(); + for (final Permission permission : permissions) { + // We don't need to provide UID and GIDs, which are only retrieved when dumping. + final LegacyPermission legacyPermission = new LegacyPermission( + permission.getPermissionInfo(), permission.getType(), 0, + EmptyArray.INT); + legacyPermissions.add(legacyPermission); + } + } + if (writePermissionOrPermissionTree == 0) { + legacyPermissionSettings.replacePermissions(legacyPermissions); + } else { + legacyPermissionSettings.replacePermissionTrees(legacyPermissions); + } + } + } + + private void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName) { + synchronized (mLock) { + mRegistry.transferPermissionsLocked(oldPackageName, newPackageName); + } + } + + private boolean canPropagatePermissionToInstantApp(@NonNull String permissionName) { + synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permissionName); + return bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant(); + } + } + + @NonNull + private List<LegacyPermission> getLegacyPermissions() { + synchronized (mLock) { + final List<LegacyPermission> legacyPermissions = new ArrayList<>(); + for (final Permission permission : mRegistry.getPermissionsLocked()) { + final LegacyPermission legacyPermission = new LegacyPermission( + permission.getPermissionInfo(), permission.getType(), permission.getUid(), + permission.getRawGids()); + legacyPermissions.add(legacyPermission); + } + return legacyPermissions; + } + } + + @NonNull + private Map<String, Set<String>> getAllAppOpPermissionPackages() { + synchronized (mLock) { + final ArrayMap<String, ArraySet<String>> appOpPermissionPackages = + mRegistry.getAllAppOpPermissionPackagesLocked(); + final Map<String, Set<String>> deepClone = new ArrayMap<>(); + final int appOpPermissionPackagesSize = appOpPermissionPackages.size(); + for (int i = 0; i < appOpPermissionPackagesSize; i++) { + final String appOpPermission = appOpPermissionPackages.keyAt(i); + final ArraySet<String> packageNames = appOpPermissionPackages.valueAt(i); + deepClone.put(appOpPermission, new ArraySet<>(packageNames)); + } + return deepClone; + } + } + @NonNull private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { final LegacyPermissionState legacyState = new LegacyPermissionState(); @@ -4772,9 +4867,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionState permissionState = permissionStates.get(i); final LegacyPermissionState.PermissionState legacyPermissionState = - new LegacyPermissionState.PermissionState( - permissionState.getPermission(), permissionState.isGranted(), - permissionState.getFlags()); + new LegacyPermissionState.PermissionState(permissionState.getName(), + permissionState.isGranted(), permissionState.getFlags()); if (permissionState.isRuntime()) { legacyState.putRuntimePermissionState(legacyPermissionState, userId); } else if (userId == UserHandle.USER_SYSTEM) { @@ -4842,12 +4936,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } @Override - public void readStateFromPackageSettingsTEMP() { - PermissionManagerService.this.readStateFromPackageSettings(); + public void readLegacyPermissionStateTEMP() { + PermissionManagerService.this.readLegacyPermissionState(); } @Override - public void writeStateToPackageSettingsTEMP() { - PermissionManagerService.this.writeStateToPackageSettings(); + public void writeLegacyPermissionStateTEMP() { + PermissionManagerService.this.writeLegacyPermissionState(); } @Override public void onUserRemoved(@UserIdInt int userId) { @@ -4950,17 +5044,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void enforceGrantRevokeRuntimePermissionPermissions(String message) { - PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message); - } - @Override - public PermissionSettings getPermissionSettings() { - return mSettings; - } - @Override - public BasePermission getPermissionTEMP(String permName) { + public Permission getPermissionTEMP(String permName) { synchronized (PermissionManagerService.this.mLock) { - return mSettings.getPermissionLocked(permName); + return mRegistry.getPermissionLocked(permName); } } @@ -4970,13 +5056,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>(); synchronized (mLock) { - int numTotalPermissions = mSettings.mPermissions.size(); - - for (int i = 0; i < numTotalPermissions; i++) { - BasePermission bp = mSettings.mPermissions.valueAt(i); - - if (bp.getProtection() == protection) { - matchingPermissions.add(bp.generatePermissionInfo(0)); + for (final Permission permission : mRegistry.getPermissionsLocked()) { + if (permission.getProtection() == protection) { + matchingPermissions.add(permission.generatePermissionInfo(0)); } } } @@ -4990,13 +5072,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>(); synchronized (mLock) { - int numTotalPermissions = mSettings.mPermissions.size(); - - for (int i = 0; i < numTotalPermissions; i++) { - BasePermission bp = mSettings.mPermissions.valueAt(i); - - if ((bp.getProtectionFlags() & protectionFlags) == protectionFlags) { - matchingPermissions.add(bp.generatePermissionInfo(0)); + for (final Permission permission : mRegistry.getPermissionsLocked()) { + if ((permission.getProtectionFlags() & protectionFlags) == protectionFlags) { + matchingPermissions.add(permission.generatePermissionInfo(0)); } } } @@ -5187,25 +5265,62 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions) { + public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissionNames) { synchronized (mLock) { - Iterator<String> iterator = permissions.iterator(); + Iterator<String> iterator = permissionNames.iterator(); while (iterator.hasNext()) { - String permission = iterator.next(); - BasePermission basePermission = mSettings.mPermissions.get(permission); - if (basePermission == null || !basePermission.isHardOrSoftRestricted()) { + final String permissionName = iterator.next(); + final Permission permission = mRegistry.getPermissionLocked(permissionName); + if (permission == null || !permission.isHardOrSoftRestricted()) { iterator.remove(); } } } } + @Override + public void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + PermissionManagerService.this.readLegacyPermissions(legacyPermissionSettings); + } + + @Override + public void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + PermissionManagerService.this.writeLegacyPermissions(legacyPermissionSettings); + } + + @Override + public void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName) { + PermissionManagerService.this.transferPermissions(oldPackageName, newPackageName); + } + + @Override + public boolean canPropagatePermissionToInstantApp(@NonNull String permissionName) { + return PermissionManagerService.this.canPropagatePermissionToInstantApp(permissionName); + } + + @NonNull + @Override + public List<LegacyPermission> getLegacyPermissions() { + return PermissionManagerService.this.getLegacyPermissions(); + } + @NonNull + @Override + public Map<String, Set<String>> getAllAppOpPermissionPackages() { + return PermissionManagerService.this.getAllAppOpPermissionPackages(); + } + + @NonNull + @Override public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { return PermissionManagerService.this.getLegacyPermissionState(appId); } @NonNull + @Override public int[] getGidsForUid(int uid) { return PermissionManagerService.this.getGidsForUid(uid); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 20e9c5dcb521..df81bac99a21 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -280,21 +280,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** - * Read permission state from package settings. + * Read legacy permission state from package settings. * * TODO(zhanghai): This is a temporary method because we should not expose * {@code PackageSetting} which is a implementation detail that permission should not know. * Instead, it should retrieve the legacy state via a defined API. */ - public abstract void readStateFromPackageSettingsTEMP(); + public abstract void readLegacyPermissionStateTEMP(); /** - * Write permission state to package settings. + * Write legacy permission state to package settings. * * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence * for permission. */ - public abstract void writeStateToPackageSettingsTEMP(); + public abstract void writeLegacyPermissionStateTEMP(); /** * Notify that a user has been removed and its permission state should be removed as well. @@ -367,16 +367,14 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, @NonNull String message); - public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message); - - public abstract @NonNull PermissionSettings getPermissionSettings(); /** Grants default browser permissions to the given package */ public abstract void grantDefaultPermissionsToDefaultBrowser( @NonNull String packageName, @UserIdInt int userId); /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ - public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); + @Nullable + public abstract Permission getPermissionTEMP(@NonNull String permName); /** Get all permissions that have a certain protection */ public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtection( @@ -536,5 +534,39 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or * {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input. */ - public abstract void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions); + public abstract void retainHardAndSoftRestrictedPermissions( + @NonNull List<String> permissionNames); + + /** + * Read legacy permissions from legacy permission settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code LegacyPermissionSettings} which is a implementation detail that permission should not + * know. Instead, it should retrieve the legacy permissions via a defined API. + */ + public abstract void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Write legacy permissions to legacy permission settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + public abstract void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Transfers ownership of permissions from one package to another. + */ + public abstract void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName); + + /** + * Check whether a permission can be propagated to instant app. + * + * @param permissionName the name of the permission + * @return whether the permission can be propagated + */ + public abstract boolean canPropagatePermissionToInstantApp(@NonNull String permissionName); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java new file mode 100644 index 000000000000..36719203e4b5 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.parsing.component.ParsedPermissionGroup; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Collection; + +/** + * Permission registry for permissions, permission trees, permission groups and related things. + */ +public class PermissionRegistry { + /** + * All of the permissions known to the system. The mapping is from permission + * name to permission object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>(); + + /** + * All permission trees known to the system. The mapping is from permission tree + * name to permission object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, Permission> mPermissionTrees = new ArrayMap<>(); + + /** + * All permisson groups know to the system. The mapping is from permission group + * name to permission group object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups = new ArrayMap<>(); + + /** + * Set of packages that request a particular app op. The mapping is from permission + * name to package names. + */ + @GuardedBy("mLock") + private final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>(); + + @NonNull + private final Object mLock; + + public PermissionRegistry(@NonNull Object lock) { + mLock = lock; + } + + @GuardedBy("mLock") + @NonNull + public Collection<Permission> getPermissionsLocked() { + return mPermissions.values(); + } + + @GuardedBy("mLock") + @Nullable + public Permission getPermissionLocked(@NonNull String permName) { + return mPermissions.get(permName); + } + + @Nullable + public Permission getPermission(@NonNull String permissionName) { + synchronized (mLock) { + return getPermissionLocked(permissionName); + } + } + + @GuardedBy("mLock") + public void addPermissionLocked(@NonNull Permission permission) { + mPermissions.put(permission.getName(), permission); + } + + @GuardedBy("mLock") + public void removePermissionLocked(@NonNull String permissionName) { + mPermissions.remove(permissionName); + } + + @GuardedBy("mLock") + @NonNull + public Collection<Permission> getPermissionTreesLocked() { + return mPermissionTrees.values(); + } + + @GuardedBy("mLock") + @Nullable + public Permission getPermissionTreeLocked(@NonNull String permissionTreeName) { + return mPermissionTrees.get(permissionTreeName); + } + + @GuardedBy("mLock") + public void addPermissionTreeLocked(@NonNull Permission permissionTree) { + mPermissionTrees.put(permissionTree.getName(), permissionTree); + } + + /** + * Transfers ownership of permissions from one package to another. + */ + public void transferPermissionsLocked(@NonNull String oldPackageName, + @NonNull String newPackageName) { + for (int i = 0; i < 2; i++) { + ArrayMap<String, Permission> permissions = i == 0 ? mPermissionTrees : mPermissions; + for (final Permission permission : permissions.values()) { + permission.transfer(oldPackageName, newPackageName); + } + } + } + + @GuardedBy("mLock") + @NonNull + public Collection<ParsedPermissionGroup> getPermissionGroupsLocked() { + return mPermissionGroups.values(); + } + + @GuardedBy("mLock") + @Nullable + public ParsedPermissionGroup getPermissionGroupLocked(@NonNull String permissionGroupName) { + return mPermissionGroups.get(permissionGroupName); + } + + @GuardedBy("mLock") + public void addPermissionGroupLocked(@NonNull ParsedPermissionGroup permissionGroup) { + mPermissionGroups.put(permissionGroup.getName(), permissionGroup); + } + + @GuardedBy("mLock") + @NonNull + public ArrayMap<String, ArraySet<String>> getAllAppOpPermissionPackagesLocked() { + return mAppOpPermissionPackages; + } + + @GuardedBy("mLock") + @Nullable + public ArraySet<String> getAppOpPermissionPackagesLocked(@NonNull String permissionName) { + return mAppOpPermissionPackages.get(permissionName); + } + + @GuardedBy("mLock") + public void addAppOpPermissionPackageLocked(@NonNull String permissionName, + @NonNull String packageName) { + ArraySet<String> packageNames = mAppOpPermissionPackages.get(permissionName); + if (packageNames == null) { + packageNames = new ArraySet<>(); + mAppOpPermissionPackages.put(permissionName, packageNames); + } + packageNames.add(packageName); + } + + @GuardedBy("mLock") + public void removeAppOpPermissionPackageLocked(@NonNull String permissionName, + @NonNull String packageName) { + final ArraySet<String> packageNames = mAppOpPermissionPackages.get(permissionName); + if (packageNames == null) { + return; + } + final boolean removed = packageNames.remove(packageName); + if (removed && packageNames.isEmpty()) { + mAppOpPermissionPackages.remove(permissionName); + } + } + + /** + * Returns the permission tree for the given permission. + * @throws SecurityException If the calling UID is not allowed to add permissions to the + * found permission tree. + */ + @NonNull + public Permission enforcePermissionTree(@NonNull String permissionName, int callingUid) { + synchronized (mLock) { + return Permission.enforcePermissionTree(mPermissionTrees.values(), permissionName, + callingUid); + } + } + + public boolean isPermissionInstant(@NonNull String permissionName) { + synchronized (mLock) { + final Permission permission = mPermissions.get(permissionName); + return permission != null && permission.isInstant(); + } + } + + boolean isPermissionAppOp(@NonNull String permissionName) { + synchronized (mLock) { + final Permission permission = mPermissions.get(permissionName); + return permission != null && permission.isAppOp(); + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java deleted file mode 100644 index eea8ac737b86..000000000000 --- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.pm.permission; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.pm.parsing.component.ParsedPermissionGroup; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.XmlUtils; -import com.android.server.pm.DumpState; -import com.android.server.pm.PackageManagerService; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Collection; - -/** - * Permissions and other related data. This class is not meant for - * direct access outside of the permission package with the sole exception - * of package settings. Instead, it should be reference either from the - * permission manager or package settings. - */ -public class PermissionSettings { - - /** - * All of the permissions known to the system. The mapping is from permission - * name to permission object. - */ - @GuardedBy("mLock") - final ArrayMap<String, BasePermission> mPermissions = - new ArrayMap<String, BasePermission>(); - - /** - * All permission trees known to the system. The mapping is from permission tree - * name to permission object. - */ - @GuardedBy("mLock") - final ArrayMap<String, BasePermission> mPermissionTrees = - new ArrayMap<String, BasePermission>(); - - /** - * All permisson groups know to the system. The mapping is from permission group - * name to permission group object. - */ - @GuardedBy("mLock") - final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups = - new ArrayMap<>(); - - /** - * Set of packages that request a particular app op. The mapping is from permission - * name to package names. - */ - @GuardedBy("mLock") - final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>(); - - private final Object mLock; - - PermissionSettings(@NonNull Object lock) { - mLock = lock; - } - - public @Nullable BasePermission getPermission(@NonNull String permName) { - synchronized (mLock) { - return getPermissionLocked(permName); - } - } - - public void addAppOpPackage(String permName, String packageName) { - synchronized (mLock) { - ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName); - if (pkgs == null) { - pkgs = new ArraySet<>(); - mAppOpPermissionPackages.put(permName, pkgs); - } - pkgs.add(packageName); - } - } - - /** - * Transfers ownership of permissions from one package to another. - */ - public void transferPermissions(String origPackageName, String newPackageName) { - synchronized (mLock) { - for (int i=0; i<2; i++) { - ArrayMap<String, BasePermission> permissions = - i == 0 ? mPermissionTrees : mPermissions; - for (BasePermission bp : permissions.values()) { - bp.transfer(origPackageName, newPackageName); - } - } - } - } - - public boolean canPropagatePermissionToInstantApp(String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()); - } - } - - public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException { - synchronized (mLock) { - readPermissions(mPermissions, parser); - } - } - - public void readPermissionTrees(XmlPullParser parser) - throws IOException, XmlPullParserException { - synchronized (mLock) { - readPermissions(mPermissionTrees, parser); - } - } - - public void writePermissions(XmlSerializer serializer) throws IOException { - synchronized (mLock) { - for (BasePermission bp : mPermissions.values()) { - bp.writeLPr(serializer); - } - } - } - - public void writePermissionTrees(XmlSerializer serializer) throws IOException { - synchronized (mLock) { - for (BasePermission bp : mPermissionTrees.values()) { - bp.writeLPr(serializer); - } - } - } - - public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - if (!BasePermission.readLPw(out, parser)) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element reading permissions: " + parser.getName() + " at " - + parser.getPositionDescription()); - } - XmlUtils.skipCurrentTag(parser); - } - } - - public void dumpPermissions(PrintWriter pw, String packageName, - ArraySet<String> permissionNames, boolean externalStorageEnforced, - DumpState dumpState) { - synchronized (mLock) { - boolean printedSomething = false; - for (BasePermission bp : mPermissions.values()) { - printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames, - externalStorageEnforced, printedSomething, dumpState); - } - if (packageName == null && permissionNames == null) { - for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) { - if (iperm == 0) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("AppOp Permissions:"); - } - pw.print(" AppOp Permission "); - pw.print(mAppOpPermissionPackages.keyAt(iperm)); - pw.println(":"); - ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm); - for (int ipkg=0; ipkg<pkgs.size(); ipkg++) { - pw.print(" "); pw.println(pkgs.valueAt(ipkg)); - } - } - } - } - } - - @GuardedBy("mLock") - @Nullable BasePermission getPermissionLocked(@NonNull String permName) { - return mPermissions.get(permName); - } - - @GuardedBy("mLock") - @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) { - return mPermissionTrees.get(permName); - } - - @GuardedBy("mLock") - void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) { - mPermissions.put(permName, permission); - } - - @GuardedBy("mLock") - void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) { - mPermissionTrees.put(permName, permission); - } - - @GuardedBy("mLock") - void removePermissionLocked(@NonNull String permName) { - mPermissions.remove(permName); - } - - @GuardedBy("mLock") - void removePermissionTreeLocked(@NonNull String permName) { - mPermissionTrees.remove(permName); - } - - @GuardedBy("mLock") - @NonNull Collection<BasePermission> getAllPermissionsLocked() { - return mPermissions.values(); - } - - @GuardedBy("mLock") - @NonNull Collection<BasePermission> getAllPermissionTreesLocked() { - return mPermissionTrees.values(); - } - - /** - * Returns the permission tree for the given permission. - * @throws SecurityException If the calling UID is not allowed to add permissions to the - * found permission tree. - */ - @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) { - synchronized (mLock) { - return BasePermission.enforcePermissionTree( - mPermissionTrees.values(), permName, callingUid); - } - } - - /** - * Check whether a permission is runtime. - * - * @see BasePermission#isRuntime() - */ - public boolean isPermissionRuntime(@NonNull String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && bp.isRuntime()); - } - } - - public boolean isPermissionInstant(String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && bp.isInstant()); - } - } - - boolean isPermissionAppOp(String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && bp.isAppOp()); - } - } - -} diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java index 59b204f7dfff..12f29d091134 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionState.java @@ -27,7 +27,7 @@ import com.android.internal.annotations.GuardedBy; public final class PermissionState { @NonNull - private final BasePermission mPermission; + private final Permission mPermission; private final Object mLock = new Object(); @@ -40,7 +40,7 @@ public final class PermissionState { @GuardedBy("mLock") private int mFlags; - public PermissionState(@NonNull BasePermission permission) { + public PermissionState(@NonNull Permission permission) { mPermission = permission; } @@ -52,7 +52,7 @@ public final class PermissionState { } @NonNull - public BasePermission getPermission() { + public Permission getPermission() { return mPermission; } diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java index 9727a5452440..04d82e872ae6 100644 --- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java +++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java @@ -125,7 +125,7 @@ public final class UidPermissionState { } @NonNull - private PermissionState getOrCreatePermissionState(@NonNull BasePermission permission) { + private PermissionState getOrCreatePermissionState(@NonNull Permission permission) { if (mPermissions == null) { mPermissions = new ArrayMap<>(); } @@ -158,7 +158,7 @@ public final class UidPermissionState { * @param granted whether the permission is granted * @param flags the permission flags */ - public void putPermissionState(@NonNull BasePermission permission, boolean granted, int flags) { + public void putPermissionState(@NonNull Permission permission, boolean granted, int flags) { final String name = permission.getName(); if (mPermissions == null) { mPermissions = new ArrayMap<>(); @@ -230,7 +230,7 @@ public final class UidPermissionState { * @param permission the permission to grant * @return whether the permission grant state changed */ - public boolean grantPermission(@NonNull BasePermission permission) { + public boolean grantPermission(@NonNull Permission permission) { final PermissionState permissionState = getOrCreatePermissionState(permission); return permissionState.grant(); } @@ -241,7 +241,7 @@ public final class UidPermissionState { * @param permission the permission to revoke * @return whether the permission grant state changed */ - public boolean revokePermission(@NonNull BasePermission permission) { + public boolean revokePermission(@NonNull Permission permission) { final String name = permission.getName(); final PermissionState permissionState = getPermissionState(name); if (permissionState == null) { @@ -276,7 +276,7 @@ public final class UidPermissionState { * @param flagValues the new values for the masked flags * @return whether the permission flags changed */ - public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask, + public boolean updatePermissionFlags(@NonNull Permission permission, int flagMask, int flagValues) { if (flagMask == 0) { return false; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0983e10d2965..8b677a99b22a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1468,10 +1468,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; if (mHasFeatureLeanback) { isSetupComplete &= isTvUserSetupComplete(); + } else if (mHasFeatureAuto) { + isSetupComplete &= isAutoUserSetupComplete(); } return isSetupComplete; } + private boolean isAutoUserSetupComplete() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + "android.car.SETUP_WIZARD_IN_PROGRESS", 0, UserHandle.USER_CURRENT) == 0; + } + private boolean isTvUserSetupComplete() { return Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 61b672d6777c..a02701f8ad00 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4172,13 +4172,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public int getMaxNumPictureInPictureActions(IBinder token) { - // Currently, this is a static constant, but later, we may change this to be dependent on - // the context of the activity - return 3; - } - /** * Checks the state of the system and the activity associated with the given {@param token} to * verify that picture-in-picture is supported for that activity. @@ -4216,7 +4209,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } // Truncate the number of actions if necessary - params.truncateActions(getMaxNumPictureInPictureActions(token)); + params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext)); return r; } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 5e1a26b91863..ce20dbdb2997 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -397,6 +397,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } void setOrganizer(IDisplayAreaOrganizer organizer) { + setOrganizer(organizer, false /* skipDisplayAreaAppeared */); + } + + void setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared) { if (mOrganizer == organizer) return; IDisplayAreaOrganizer lastOrganizer = mOrganizer; // Update the new display area organizer before calling sendDisplayAreaVanished since it @@ -404,7 +408,9 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { // about it. mOrganizer = organizer; sendDisplayAreaVanished(lastOrganizer); - sendDisplayAreaAppeared(); + if (!skipDisplayAreaAppeared) { + sendDisplayAreaAppeared(); + } } void sendDisplayAreaAppeared() { diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 667f3dc9d8d3..43b9a218d072 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -18,16 +18,20 @@ package com.android.server.wm; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; +import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; +import android.window.DisplayAreaAppearedInfo; import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; import com.android.internal.protolog.common.ProtoLog; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub { private static final String TAG = "DisplayAreaOrganizerController"; @@ -64,7 +68,8 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } @Override - public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) { + public ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer( + IDisplayAreaOrganizer organizer, int feature) { enforceTaskPermission("registerOrganizer()"); final long uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); @@ -83,12 +88,18 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } catch (RemoteException e) { // Oh well... } + + final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>(); mService.mRootWindowContainer.forAllDisplayAreas((da) -> { if (da.mFeatureId != feature) return; - da.setOrganizer(organizer); + da.setOrganizer(organizer, true /* skipDisplayAreaAppeared */); + displayAreaInfos.add(new DisplayAreaAppearedInfo(da.getDisplayAreaInfo(), + new SurfaceControl(da.getSurfaceControl(), + "DisplayAreaOrganizerController.registerOrganizer"))); }); mOrganizersByFeatureIds.put(feature, organizer); + return new ParceledListSlice<>(displayAreaInfos); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 7ed22a1f7777..1a27b1bc4a6e 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -181,9 +181,25 @@ class ScreenRotationAnimation { mSurfaceRotationAnimationController = new SurfaceRotationAnimationController(); // Check whether the current screen contains any secure content. - final boolean isSecure = displayContent.hasSecureWindowOnScreen(); + boolean isSecure = displayContent.hasSecureWindowOnScreen(); + final int displayId = displayContent.getDisplayId(); final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); + try { + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = + mService.mDisplayManagerInternal.systemScreenshot(displayId); + if (screenshotBuffer == null) { + Slog.w(TAG, "Unable to take screenshot of display " + displayId); + return; + } + + // If the screenshot contains secure layers, we have to make sure the + // screenshot surface we display it in also has FLAG_SECURE so that + // the user can not screenshot secure layers via the screenshot surface. + if (screenshotBuffer.containsSecureLayers()) { + isSecure = true; + } + mBackColorSurface = displayContent.makeChildSurface(null) .setName("BackColorSurface") .setColorLayer() @@ -203,51 +219,39 @@ class ScreenRotationAnimation { .setCallsite("ScreenRotationAnimation") .build(); - // Capture a screenshot into the surface we just created. - final int displayId = displayContent.getDisplayId(); final Surface surface = mService.mSurfaceFactory.get(); - // In case display bounds change, screenshot buffer and surface may mismatch so set a - // scaling mode. + // In case display bounds change, screenshot buffer and surface may mismatch so + // set a scaling mode. surface.copyFrom(mScreenshotLayer); surface.setScalingMode(Surface.SCALING_MODE_SCALE_TO_WINDOW); - SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = - mService.mDisplayManagerInternal.systemScreenshot(displayId); - if (screenshotBuffer != null) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, - "ScreenRotationAnimation#getMedianBorderLuma"); - mStartLuma = RotationAnimationUtils.getMedianBorderLuma( - screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace()); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - try { - surface.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(), - screenshotBuffer.getColorSpace()); - } catch (RuntimeException e) { - Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage()); - } - // If the screenshot contains secure layers, we have to make sure the - // screenshot surface we display it in also has FLAG_SECURE so that - // the user can not screenshot secure layers via the screenshot surface. - if (screenshotBuffer.containsSecureLayers()) { - t.setSecure(mScreenshotLayer, true); - } - t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE); - t.reparent(mBackColorSurface, displayContent.getSurfaceControl()); - t.setLayer(mBackColorSurface, -1); - t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma}); - t.setAlpha(mBackColorSurface, 1); - t.show(mScreenshotLayer); - t.show(mBackColorSurface); - } else { - Slog.w(TAG, "Unable to take screenshot of display " + displayId); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "ScreenRotationAnimation#getMedianBorderLuma"); + mStartLuma = RotationAnimationUtils.getMedianBorderLuma( + screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace()); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + try { + surface.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(), + screenshotBuffer.getColorSpace()); + } catch (RuntimeException e) { + Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage()); } + + t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE); + t.reparent(mBackColorSurface, displayContent.getSurfaceControl()); + t.setLayer(mBackColorSurface, -1); + t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma}); + t.setAlpha(mBackColorSurface, 1); + t.show(mScreenshotLayer); + t.show(mBackColorSurface); surface.destroy(); + } catch (OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate freeze surface", e); } ProtoLog.i(WM_SHOW_SURFACE_ALLOC, - " FREEZE %s: CREATE", mScreenshotLayer); + " FREEZE %s: CREATE", mScreenshotLayer); setRotation(t, realOriginalRotation); t.apply(); } diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java index d9365c5d0666..6f434e05c800 100644 --- a/services/core/java/com/android/server/wm/SurfaceFreezer.java +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -137,6 +137,7 @@ class SurfaceFreezer { new SurfaceControl.LayerCaptureArgs.Builder(target) .setSourceCrop(cropBounds) .setCaptureSecureLayers(true) + .setAllowProtected(true) .build(); return SurfaceControl.captureLayers(captureArgs); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3f15ff8ebd25..ab2f42426153 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2176,6 +2176,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final DisplayContent dc = getDisplayContent(); if (isInputMethodTarget()) { + // Make sure to set mInputMethodTarget as null when the removed window is the IME + // target, in case computeImeTarget may use the outdated target. + dc.mInputMethodTarget = null; dc.computeImeTarget(true /* updateImeTarget */); } if (dc.mInputMethodInputTarget == this) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 871364569839..1e959e54555f 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -631,25 +631,25 @@ class WindowStateAnimator { } private boolean shouldConsumeMainWindowSizeTransaction() { - // If we use BLASTSync we always consume the transaction when finishing - // the sync. - if (mService.useBLASTSync()) { - return false; - } - // We only consume the transaction when the client is calling relayout - // because this is the only time we know the frameNumber will be valid - // due to the client renderer being paused. Put otherwise, only when - // mInRelayout is true can we guarantee the next frame will contain - // the most recent configuration. - if (!mWin.mInRelayout) return false; - // Since we can only do this for one window, we focus on the main application window - if (mAttrType != TYPE_BASE_APPLICATION) return false; - final Task task = mWin.getTask(); - if (task == null) return false; - if (task.getMainWindowSizeChangeTransaction() == null) return false; - // Likewise we only focus on the task root, since we can only use one window - if (!mWin.mActivityRecord.isRootOfTask()) return false; - return true; + // If we use BLASTSync we always consume the transaction when finishing + // the sync. + if (mService.useBLASTSync() && mWin.useBLASTSync()) { + return false; + } + // We only consume the transaction when the client is calling relayout + // because this is the only time we know the frameNumber will be valid + // due to the client renderer being paused. Put otherwise, only when + // mInRelayout is true can we guarantee the next frame will contain + // the most recent configuration. + if (!mWin.mInRelayout) return false; + // Since we can only do this for one window, we focus on the main application window + if (mAttrType != TYPE_BASE_APPLICATION) return false; + final Task task = mWin.getTask(); + if (task == null) return false; + if (task.getMainWindowSizeChangeTransaction() == null) return false; + // Likewise we only focus on the task root, since we can only use one window + if (!mWin.mActivityRecord.isRootOfTask()) return false; + return true; } void setSurfaceBoundariesLocked(SurfaceControl.Transaction t, final boolean recoveringMemory) { diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java index 141561839ce3..261dda7ab15d 100644 --- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java +++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java @@ -17,6 +17,7 @@ package com.android.server.wm.utils; import static android.hardware.HardwareBuffer.RGBA_8888; +import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT; import android.graphics.Color; import android.graphics.ColorSpace; @@ -38,12 +39,21 @@ import java.util.Arrays; public class RotationAnimationUtils { /** + * @return whether the hardwareBuffer passed in is marked as protected. + */ + public static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) { + return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT; + } + + /** * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the * luminance at the borders of the bitmap * @return the average luminance of all the pixels at the borders of the bitmap */ public static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) { - if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888) { + // Cannot read content from buffer with protected usage. + if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888 + || hasProtectedContent(hardwareBuffer)) { return 0; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 9f83bafbed75..aac60b406ca8 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -27,7 +27,6 @@ cc_library_static { "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", - "com_android_server_connectivity_Vpn.cpp", "com_android_server_ConsumerIrService.cpp", "com_android_server_devicepolicy_CryptoTestHelper.cpp", "com_android_server_gpu_GpuService.cpp", @@ -62,6 +61,8 @@ cc_library_static { "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp", "onload.cpp", ":lib_networkStatsFactory_native", + // TODO: move the file below to the connectivity APEX + "com_android_server_connectivity_Vpn.cpp", ], include_dirs: [ diff --git a/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp b/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp index 6faffb886209..e994c03a9239 100644 --- a/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp +++ b/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp @@ -78,12 +78,6 @@ static const JNINativeMethod method_table[] = { reinterpret_cast<void*>(convertSurfaceToNativeHandle)}, }; -int register_android_server_FingerprintService(JNIEnv* env) { - return jniRegisterNativeMethods(env, - "com/android/server/biometrics/sensors/fingerprint/FingerprintService", - method_table, NELEM(method_table)); -} - int register_android_server_FaceService(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/server/biometrics/sensors/face/FaceService", method_table, NELEM(method_table)); diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 0ffa5c39bacc..79b5fed3e448 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -62,7 +62,6 @@ int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTrac int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); int register_android_server_AdbDebuggingManager(JNIEnv* env); -int register_android_server_FingerprintService(JNIEnv* env); int register_android_server_FaceService(JNIEnv* env); int register_android_server_GpuService(JNIEnv* env); }; @@ -120,7 +119,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); register_android_server_stats_pull_StatsPullAtomService(env); register_android_server_AdbDebuggingManager(env); - register_android_server_FingerprintService(env); register_android_server_FaceService(env); register_android_server_GpuService(env); return JNI_VERSION_1_4; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9edd7fbbb84c..4d553e2f92aa 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3240,7 +3240,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(adminReceiver, "ComponentName is null"); - enforceShell("forceRemoveActiveAdmin"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), + "Non-shell user attempted to call forceRemoveActiveAdmin"); mInjector.binderWithCleanCallingIdentity(() -> { synchronized (getLockObject()) { if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) { @@ -3319,13 +3320,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return (admin != null) && admin.testOnlyAdmin; } - private void enforceShell(String method) { - final int callingUid = mInjector.binderGetCallingUid(); - if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) { - throw new SecurityException("Non-shell user attempted to call " + method); - } - } - @Override public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) { if (!mHasFeature) { @@ -7255,10 +7249,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new IllegalArgumentException("Invalid component " + admin + " for device owner"); } - final boolean hasIncompatibleAccountsOrNonAdb = - hasIncompatibleAccountsOrNonAdbNoLock(userId, admin); + + final CallerIdentity caller = getCallerIdentity(); synchronized (getLockObject()) { - enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb); + enforceCanSetDeviceOwnerLocked(caller, admin, userId); final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId); if (activeAdmin == null || getUserData(userId).mRemovingAdmins.contains(admin)) { @@ -7267,7 +7261,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Shutting down backup manager service permanently. toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false); - if (isAdb()) { + if (isAdb(caller)) { // Log device owner provisioning was started using adb. MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER); DevicePolicyEventLogger @@ -7623,10 +7617,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " not installed for userId:" + userHandle); } - final boolean hasIncompatibleAccountsOrNonAdb = - hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who); + final CallerIdentity caller = getCallerIdentity(); synchronized (getLockObject()) { - enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb); + enforceCanSetProfileOwnerLocked(caller, who, userHandle); final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin == null || getUserData(userHandle).mRemovingAdmins.contains(who)) { @@ -7644,7 +7637,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - if (isAdb()) { + if (isAdb(caller)) { // Log profile owner provisioning was started using adb. MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER); DevicePolicyEventLogger @@ -7837,6 +7830,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } + final CallerIdentity caller = getCallerIdentity(); if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle) && getManagedUserId(userHandle) == -1) { // No managed device, user or profile, so setting provisioning state makes no sense. @@ -7848,7 +7842,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean transitionCheckNeeded = true; // Calling identity/permission checks. - if (isAdb()) { + if (isAdb(caller)) { // ADB shell can only move directly from un-managed to finalized as part of directly // setting profile-owner or device-owner. if (getUserProvisioningState(userHandle) != @@ -8211,8 +8205,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * - SYSTEM_UID * - adb unless hasIncompatibleAccountsOrNonAdb is true. */ - private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle, - boolean hasIncompatibleAccountsOrNonAdb) { + private void enforceCanSetProfileOwnerLocked(CallerIdentity caller, + @Nullable ComponentName owner, int userHandle) { UserInfo info = getUserInfo(userHandle); if (info == null) { // User doesn't exist. @@ -8230,9 +8224,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new IllegalStateException("Trying to set the profile owner, but the user " + "already has a device owner."); } - if (isAdb()) { + if (isAdb(caller)) { if ((mIsWatch || hasUserSetupCompleted(userHandle)) - && hasIncompatibleAccountsOrNonAdb) { + && hasIncompatibleAccountsOrNonAdbNoLock(caller, userHandle, owner)) { throw new IllegalStateException("Not allowed to set the profile owner because " + "there are already some accounts on the profile"); } @@ -8271,16 +8265,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS * permission. */ - private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, - @UserIdInt int userId, - boolean hasIncompatibleAccountsOrNonAdb) { - if (!isAdb()) { + private void enforceCanSetDeviceOwnerLocked(CallerIdentity caller, + @Nullable ComponentName owner, @UserIdInt int userId) { + if (!isAdb(caller)) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); } - final int code = checkDeviceOwnerProvisioningPreConditionLocked( - owner, userId, isAdb(), hasIncompatibleAccountsOrNonAdb); + final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner, userId, + isAdb(caller), hasIncompatibleAccountsOrNonAdbNoLock(caller, userId, owner)); if (code != CODE_OK) { throw new IllegalStateException(computeProvisioningErrorString(code, userId)); } @@ -8524,6 +8517,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.print("mUserControlDisabledPackages="); pw.println(policy.mUserControlDisabledPackages); pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended); + pw.print("mUserSetupComplete="); pw.println(policy.mUserSetupComplete); pw.decreaseIndent(); } } @@ -11668,7 +11662,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearSystemUpdatePolicyFreezePeriodRecord() { - enforceShell("clearSystemUpdatePolicyFreezePeriodRecord"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), + "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord"); synchronized (getLockObject()) { // Print out current record to help diagnosed CTS failures Slog.i(LOG_TAG, "Clear freeze period record: " @@ -12578,23 +12573,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) { - // As the caller is the system, it must specify the component name of the profile owner - // as a safety check. - Objects.requireNonNull(who); - if (!mHasFeature) { return; } + // As the caller is the system, it must specify the component name of the profile owner + // as a safety check. + Objects.requireNonNull(who); + final CallerIdentity caller = getCallerIdentity(); // Only adb or system apps with the right permission can mark a profile owner on // organization-owned device. - if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) { + if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) { throw new SecurityException( "Only the system can mark a profile owner of organization-owned device."); } - if (isAdb()) { - if (hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) { + if (isAdb(caller)) { + if (hasIncompatibleAccountsOrNonAdbNoLock(caller, userId, who)) { throw new SecurityException( "Can only be called from ADB if the device has no accounts."); } @@ -12915,7 +12910,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceSecurityLogs() { - enforceShell("forceSecurityLogs"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), + "Non-shell user attempted to call forceSecurityLogs"); if (!mInjector.securityLogGetLoggingEnabledProperty()) { throw new IllegalStateException("logging is not available"); } @@ -13283,9 +13279,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * * DO NOT CALL IT WITH THE DPMS LOCK HELD. */ - private boolean hasIncompatibleAccountsOrNonAdbNoLock( + private boolean hasIncompatibleAccountsOrNonAdbNoLock(CallerIdentity caller, int userId, @Nullable ComponentName owner) { - if (!isAdb()) { + if (!isAdb(caller)) { return true; } wtfIfInLock(); @@ -13340,9 +13336,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean isAdb() { - final int callingUid = mInjector.binderGetCallingUid(); - return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; + private boolean isAdb(CallerIdentity caller) { + return isShellUid(caller) || isRootUid(caller); } @Override @@ -13404,7 +13399,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceNetworkLogs() { - enforceShell("forceNetworkLogs"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), + "Non-shell user attempted to call forceNetworkLogs"); synchronized (getLockObject()) { if (!isNetworkLoggingEnabledInternalLocked()) { throw new IllegalStateException("logging is not available"); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e116a353c723..03edc585b46a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1131,6 +1131,7 @@ public final class SystemServer implements Dumpable { IStorageManager storageManager = null; NetworkManagementService networkManagement = null; IpSecService ipSecService = null; + VcnManagementService vcnManagement = null; NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; IConnectivityManager connectivity = null; @@ -1581,6 +1582,15 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("StartVcnManagementService"); + try { + vcnManagement = VcnManagementService.create(context); + ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); + } + t.traceEnd(); + t.traceBegin("StartTextServicesManager"); mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); t.traceEnd(); @@ -2371,6 +2381,7 @@ public final class SystemServer implements Dumpable { final MediaRouterService mediaRouterF = mediaRouter; final MmsServiceBroker mmsServiceF = mmsService; final IpSecService ipSecServiceF = ipSecService; + final VcnManagementService vcnManagementF = vcnManagement; final WindowManagerService windowManagerF = wm; final ConnectivityManager connectivityF = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -2463,6 +2474,15 @@ public final class SystemServer implements Dumpable { reportWtf("making IpSec Service ready", e); } t.traceEnd(); + t.traceBegin("MakeVcnManagementServiceReady"); + try { + if (vcnManagementF != null) { + vcnManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VcnManagementService ready", e); + } + t.traceEnd(); t.traceBegin("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) { diff --git a/services/net/Android.bp b/services/net/Android.bp index 3c9322d2dfdc..1ccd512a9daf 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -66,5 +66,8 @@ filegroup { ":framework-annotations", "java/android/net/util/NetworkConstants.java", ], - visibility: ["//frameworks/base/packages/Tethering"], + visibility: [ + "//frameworks/base/packages/Tethering", + "//packages/modules/Connectivity/Tethering" + ], } 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 a5f083477863..db4aba519a6a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -17,8 +17,14 @@ package com.android.server.alarm; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; +import static android.app.AlarmManager.FLAG_IDLE_UNTIL; +import static android.app.AlarmManager.FLAG_STANDALONE; +import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; +import static android.app.AlarmManager.WINDOW_EXACT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; @@ -66,7 +72,6 @@ import static org.mockito.Mockito.atLeastOnce; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.AlarmManager; import android.app.IActivityManager; import android.app.IAlarmCompleteListener; import android.app.IAlarmListener; @@ -85,7 +90,6 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.provider.Settings; -import android.util.IndentingPrintWriter; import android.util.Log; import android.util.SparseArray; @@ -112,8 +116,6 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.Executor; @@ -354,48 +356,51 @@ public class AlarmManagerServiceTest { } private void setTestAlarm(int type, long triggerTime, PendingIntent operation) { - setTestAlarm(type, triggerTime, operation, 0, AlarmManager.FLAG_STANDALONE, - TEST_CALLING_UID); + setTestAlarm(type, triggerTime, operation, 0, FLAG_STANDALONE, TEST_CALLING_UID); } private void setRepeatingTestAlarm(int type, long firstTrigger, long interval, PendingIntent pi) { - setTestAlarm(type, firstTrigger, pi, interval, AlarmManager.FLAG_STANDALONE, - TEST_CALLING_UID); + setTestAlarm(type, firstTrigger, pi, interval, FLAG_STANDALONE, TEST_CALLING_UID); } private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) { - setTestAlarm(type, triggerTime, pi, 0, AlarmManager.FLAG_IDLE_UNTIL, TEST_CALLING_UID); + setTestAlarm(type, triggerTime, pi, 0, FLAG_IDLE_UNTIL, TEST_CALLING_UID); } private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) { // Note: Only alarm clock alarms are allowed to include this flag in the actual service. // But this is a unit test so we'll only test the flag for granularity and convenience. - setTestAlarm(type, triggerTime, pi, 0, - AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE, TEST_CALLING_UID); + setTestAlarm(type, triggerTime, pi, 0, FLAG_WAKE_FROM_IDLE | FLAG_STANDALONE, + TEST_CALLING_UID); + } + + private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi, + boolean unrestricted) { + final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE; + setTestAlarm(type, triggerTime, pi, 0, flags, TEST_CALLING_UID); } private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval, int flags, int callingUid) { - mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, operation, null, - "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE); + mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags, + null, null, callingUid, TEST_CALLING_PACKAGE); } private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) { - mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, 0, - null, listener, "test", AlarmManager.FLAG_STANDALONE, null, null, - TEST_CALLING_UID, TEST_CALLING_PACKAGE); + mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test", + FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE); } private PendingIntent getNewMockPendingIntent() { - return getNewMockPendingIntent(TEST_CALLING_UID); + return getNewMockPendingIntent(TEST_CALLING_UID, TEST_CALLING_PACKAGE); } - private PendingIntent getNewMockPendingIntent(int mockUid) { + private PendingIntent getNewMockPendingIntent(int creatorUid, String creatorPackage) { final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS); - when(mockPi.getCreatorUid()).thenReturn(mockUid); - when(mockPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE); + when(mockPi.getCreatorUid()).thenReturn(creatorUid); + when(mockPi.getCreatorPackage()).thenReturn(creatorPackage); return mockPi; } @@ -865,7 +870,7 @@ public class AlarmManagerServiceTest { public void alarmCountKeyedOnCallingUid() { final int mockCreatorUid = 431412; setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 5, - getNewMockPendingIntent(mockCreatorUid)); + getNewMockPendingIntent(mockCreatorUid, TEST_CALLING_PACKAGE)); assertEquals(1, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(-1, mService.mAlarmsPerUid.get(mockCreatorUid, -1)); } @@ -1018,7 +1023,8 @@ public class AlarmManagerServiceTest { for (int i = 0; i < numAlarms; i++) { int mockUid = UserHandle.getUid(mockUserId, 1234 + i); setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, - getNewMockPendingIntent(mockUid), 0, AlarmManager.FLAG_STANDALONE, mockUid); + getNewMockPendingIntent(mockUid, TEST_CALLING_PACKAGE), 0, FLAG_STANDALONE, + mockUid); } assertEquals(numAlarms, mService.mAlarmsPerUid.size()); mService.removeUserLocked(mockUserId); @@ -1026,26 +1032,6 @@ public class AlarmManagerServiceTest { } @Test - public void alarmCountOnRemoveFromPendingWhileIdle() { - mService.mPendingIdleUntil = mock(Alarm.class); - final int numAlarms = 15; - final PendingIntent[] pis = new PendingIntent[numAlarms]; - for (int i = 0; i < numAlarms; i++) { - pis[i] = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]); - } - assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); - assertEquals(numAlarms, mService.mPendingWhileIdleAlarms.size()); - final int toRemove = 8; - for (int i = 0; i < toRemove; i++) { - mService.removeLocked(pis[i], null); - assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0)); - } - mService.removeLocked(TEST_CALLING_UID); - assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0)); - } - - @Test public void alarmCountOnAlarmRemoved() { final int numAlarms = 10; final PendingIntent[] pis = new PendingIntent[numAlarms]; @@ -1268,6 +1254,134 @@ public class AlarmManagerServiceTest { assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed()); } + @Test + public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final long awiDelayForTest = 23; + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 0); + + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000, + getNewMockPendingIntent()); + assertNotNull(mService.mPendingIdleUntil); + + final long seedTrigger = mNowElapsedTest + 3; + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + pis[i] = getNewMockPendingIntent(); + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, seedTrigger + i * i, pis[i], false); + } + + long lastAwiDispatch = -1; + int i = 0; + while (i < numAlarms) { + final long nextDispatch = (lastAwiDispatch >= 0) ? (lastAwiDispatch + awiDelayForTest) + : (seedTrigger + i * i); + assertEquals("Wrong allow-while-idle dispatch", nextDispatch, mTestTimer.getElapsed()); + + mNowElapsedTest = nextDispatch; + mTestTimer.expire(); + + while (i < numAlarms && (seedTrigger + i * i) <= nextDispatch) { + verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(), + any(Handler.class), isNull(), any()); + i++; + } + Log.d(TAG, "Dispatched alarms upto " + i + " at " + nextDispatch); + lastAwiDispatch = nextDispatch; + } + } + + @Test + public void allowWhileIdleUnrestricted() throws Exception { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final long awiDelayForTest = 127; + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 0); + + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000, + getNewMockPendingIntent()); + assertNotNull(mService.mPendingIdleUntil); + + final long seedTrigger = mNowElapsedTest + 3; + for (int i = 1; i <= 5; i++) { + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, seedTrigger + i * i, + getNewMockPendingIntent(), true); + } + for (int i = 1; i <= 5; i++) { + final long nextTrigger = mTestTimer.getElapsed(); + assertEquals("Wrong trigger for alarm " + i, seedTrigger + i * i, nextTrigger); + mNowElapsedTest = nextTrigger; + mTestTimer.expire(); + } + } + + @Test + public void deviceIdleThrottling() throws Exception { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final long deviceIdleUntil = mNowElapsedTest + 1234; + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, deviceIdleUntil, getNewMockPendingIntent()); + + assertEquals(deviceIdleUntil, mTestTimer.getElapsed()); + + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i + 1, + pis[i] = getNewMockPendingIntent()); + assertEquals(deviceIdleUntil, mTestTimer.getElapsed()); + } + + mNowElapsedTest = mTestTimer.getElapsed(); + mTestTimer.expire(); + for (int i = 0; i < numAlarms; i++) { + verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(), + any(Handler.class), isNull(), any()); + } + } + + @Test + public void dispatchOrder() throws Exception { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final long deviceIdleUntil = mNowElapsedTest + 1234; + final PendingIntent idleUntilPi = getNewMockPendingIntent(); + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, deviceIdleUntil, idleUntilPi); + + assertEquals(deviceIdleUntil, mTestTimer.getElapsed()); + + final PendingIntent pi5wakeup = getNewMockPendingIntent(); + final PendingIntent pi4wakeupPackage = getNewMockPendingIntent(); + final PendingIntent pi2nonWakeup = getNewMockPendingIntent(57, "test.different.package"); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, pi5wakeup); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 4, pi4wakeupPackage); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 2, pi2nonWakeup); + + mNowElapsedTest = deviceIdleUntil; + mTestTimer.expire(); + + // The order of the alarms in delivery list should be: + // IdleUntil, all alarms of a package with any wakeup alarms, then the rest. + // Within a package, alarms should be ordered by requested delivery time. + final PendingIntent[] expectedOrder = new PendingIntent[]{ + idleUntilPi, pi4wakeupPackage, pi5wakeup, pi2nonWakeup}; + + ArgumentCaptor<ArrayList<Alarm>> listCaptor = ArgumentCaptor.forClass(ArrayList.class); + verify(mService).deliverAlarmsLocked(listCaptor.capture(), anyLong()); + final ArrayList<Alarm> deliveryList = listCaptor.getValue(); + + assertEquals(expectedOrder.length, deliveryList.size()); + for (int i = 0; i < expectedOrder.length; i++) { + assertTrue("Unexpected alarm: " + deliveryList.get(i) + " at pos: " + i, + deliveryList.get(i).matches(expectedOrder[i], null)); + } + } + @After public void tearDown() { if (mMockingSession != null) { @@ -1275,12 +1389,4 @@ public class AlarmManagerServiceTest { } LocalServices.removeServiceForTest(AlarmManagerInternal.class); } - - private void dumpAllAlarms(String tag, ArrayList<Alarm> alarms) { - System.out.println(tag + ": "); - IndentingPrintWriter ipw = new IndentingPrintWriter(new PrintWriter(System.out)); - AlarmManagerService.dumpAlarmList(ipw, alarms, mNowElapsedTest, - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")); - ipw.close(); - } } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java index f0490ce469dd..c4fc61a5aa6e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java @@ -20,6 +20,7 @@ import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; import static com.android.server.alarm.Constants.TEST_CALLING_UID; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -45,22 +46,17 @@ public class AlarmStoreTest { mAlarmStore = new BatchingAlarmStore(null); } - private static Alarm createAlarm(long whenElapsed, long windowLength, - AlarmManager.AlarmClockInfo alarmClock) { - return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, - alarmClock); + private static Alarm createAlarm(long whenElapsed, long windowLength) { + return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, 0); } - private static Alarm createWakeupAlarm(long whenElapsed, long windowLength, - AlarmManager.AlarmClockInfo alarmClock) { - return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, - alarmClock); + private static Alarm createWakeupAlarm(long whenElapsed, long windowLength, int flags) { + return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, flags); } - private static Alarm createAlarm(int type, long whenElapsed, long windowLength, - AlarmManager.AlarmClockInfo alarmClock) { + private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) { return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class), - null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE); } private void addAlarmsToStore(Alarm... alarms) { @@ -71,11 +67,11 @@ public class AlarmStoreTest { @Test public void add() { - final Alarm a1 = createAlarm(1, 0, null); + final Alarm a1 = createAlarm(1, 0); mAlarmStore.add(a1); assertEquals(1, mAlarmStore.size()); - final Alarm a2 = createAlarm(2, 0, null); + final Alarm a2 = createAlarm(2, 0); mAlarmStore.add(a2); assertEquals(2, mAlarmStore.size()); @@ -86,9 +82,9 @@ public class AlarmStoreTest { @Test public void remove() { - final Alarm a1 = createAlarm(1, 0, null); - final Alarm a2 = createAlarm(2, 0, null); - final Alarm a5 = createAlarm(5, 0, null); + final Alarm a1 = createAlarm(1, 0); + final Alarm a2 = createAlarm(2, 0); + final Alarm a5 = createAlarm(5, 0); addAlarmsToStore(a1, a2, a5); ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.getWhenElapsed() < 4)); @@ -96,7 +92,7 @@ public class AlarmStoreTest { assertEquals(1, mAlarmStore.size()); assertTrue(removed.contains(a1) && removed.contains(a2)); - final Alarm a8 = createAlarm(8, 0, null); + final Alarm a8 = createAlarm(8, 0); addAlarmsToStore(a8, a2, a1); removed = mAlarmStore.remove(unused -> false); @@ -110,9 +106,9 @@ public class AlarmStoreTest { @Test public void removePendingAlarms() { - final Alarm a1to11 = createAlarm(1, 10, null); - final Alarm a2to5 = createAlarm(2, 3, null); - final Alarm a6to9 = createAlarm(6, 3, null); + final Alarm a1to11 = createAlarm(1, 10); + final Alarm a2to5 = createAlarm(2, 3); + final Alarm a6to9 = createAlarm(6, 3); addAlarmsToStore(a2to5, a6to9, a1to11); final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0); @@ -134,10 +130,10 @@ public class AlarmStoreTest { @Test public void getNextWakeupDeliveryTime() { - final Alarm a1to10 = createAlarm(1, 9, null); - final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null); - final Alarm a6wakeup = createWakeupAlarm(6, 0, null); - final Alarm a5 = createAlarm(5, 0, null); + final Alarm a1to10 = createAlarm(1, 9); + final Alarm a3to8wakeup = createWakeupAlarm(3, 5, 0); + final Alarm a6wakeup = createWakeupAlarm(6, 0, 0); + final Alarm a5 = createAlarm(5, 0); addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10); // The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can @@ -155,10 +151,10 @@ public class AlarmStoreTest { @Test public void getNextDeliveryTime() { - final Alarm a1to10 = createAlarm(1, 9, null); - final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null); - final Alarm a6wakeup = createWakeupAlarm(6, 0, null); - final Alarm a5 = createAlarm(5, 0, null); + final Alarm a1to10 = createAlarm(1, 9); + final Alarm a3to8wakeup = createWakeupAlarm(3, 5, 0); + final Alarm a6wakeup = createWakeupAlarm(6, 0, 0); + final Alarm a5 = createAlarm(5, 0); addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10); assertTrue(mAlarmStore.getNextDeliveryTime() <= 5); @@ -168,10 +164,32 @@ public class AlarmStoreTest { } @Test + public void getNextWakeFromIdle() { + final Alarm a3 = createWakeupAlarm(3, 0, AlarmManager.FLAG_WAKE_FROM_IDLE); + final Alarm a5 = createWakeupAlarm(5, 0, AlarmManager.FLAG_WAKE_FROM_IDLE); + final Alarm a7 = createWakeupAlarm(7, 0, AlarmManager.FLAG_WAKE_FROM_IDLE); + + mAlarmStore.add(a5); + assertEquals(a5, mAlarmStore.getNextWakeFromIdleAlarm()); + + mAlarmStore.add(a7); + assertEquals(a5, mAlarmStore.getNextWakeFromIdleAlarm()); + + mAlarmStore.add(a3); + assertEquals(a3, mAlarmStore.getNextWakeFromIdleAlarm()); + + mAlarmStore.remove(a -> (a == a3) || (a == a5)); + assertEquals(a7, mAlarmStore.getNextWakeFromIdleAlarm()); + + mAlarmStore.remove(a -> (a == a7)); + assertNull(mAlarmStore.getNextWakeFromIdleAlarm()); + } + + @Test public void updateAlarmDeliveries() { - final Alarm a5 = createAlarm(5, 0, null); - final Alarm a8 = createAlarm(8, 0, null); - final Alarm a10 = createAlarm(10, 0, null); + final Alarm a5 = createAlarm(5, 0); + final Alarm a8 = createAlarm(8, 0); + final Alarm a10 = createAlarm(10, 0); addAlarmsToStore(a8, a10, a5); assertEquals(5, mAlarmStore.getNextDeliveryTime()); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java index efcfae38c3d3..e80f0655e641 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java @@ -19,6 +19,7 @@ package com.android.server.alarm; import static android.app.AlarmManager.ELAPSED_REALTIME; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; +import static com.android.server.alarm.Alarm.NUM_POLICIES; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; import static com.android.server.alarm.Constants.TEST_CALLING_UID; @@ -36,6 +37,8 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Random; + @Presubmit @RunWith(AndroidJUnit4.class) public class AlarmTest { @@ -49,29 +52,54 @@ public class AlarmTest { @Test public void initSetsOnlyRequesterPolicy() { final Alarm a = createDefaultAlarm(4567, 2); - assertEquals(4567, a.getPolicyElapsed(REQUESTER_POLICY_INDEX)); - assertEquals(0, a.getPolicyElapsed(APP_STANDBY_POLICY_INDEX)); + + for (int i = 0; i < NUM_POLICIES; i++) { + if (i == REQUESTER_POLICY_INDEX) { + assertEquals(4567, a.getPolicyElapsed(i)); + } else { + assertEquals(0, a.getPolicyElapsed(i)); + } + } + } + + /** + * Generates a long matrix {@code A} of size {@code NxN}, with the property that the {@code i}th + * row will have the {@code i}th element largest in that row. + * + * In other words, {@code A[i][i]} will be the maximum of {@code A[i][j]} over all {@code j}, + * {@code 0<=j<N}. + */ + private static long[][] generatePolicyTestMatrix(int n) { + final long[][] data = new long[n][n]; + final Random random = new Random(971); + for (int i = 0; i < n; i++) { + data[i][i] = 1; + for (int j = 0; j < n; j++) { + if (i != j) { + data[i][j] = random.nextInt(1 << 20); + data[i][i] += data[i][j]; + } + } + } + return data; } @Test public void whenElapsed() { final Alarm a = createDefaultAlarm(0, 0); - a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4); - a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10); - assertEquals(10, a.getWhenElapsed()); - - a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 12); - assertEquals(12, a.getWhenElapsed()); - - a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7); - assertEquals(10, a.getWhenElapsed()); - - a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 2); - assertEquals(7, a.getWhenElapsed()); - - a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 7); - assertEquals(7, a.getWhenElapsed()); + final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES); + for (int i = 0; i < NUM_POLICIES; i++) { + for (int j = 0; j < NUM_POLICIES; j++) { + a.setPolicyElapsed(j, uniqueData[i][j]); + } + assertEquals(uniqueData[i][i], a.getWhenElapsed()); + } + + for (int i = 0; i < NUM_POLICIES; i++) { + a.setPolicyElapsed(i, 3); + } + assertEquals(3, a.getWhenElapsed()); } @Test @@ -85,18 +113,21 @@ public class AlarmTest { a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 2); assertEquals(14, a.getMaxWhenElapsed()); - a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 5); - assertEquals(14, a.getMaxWhenElapsed()); - - a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 16); - assertEquals(16, a.getMaxWhenElapsed()); - - a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 12); - assertEquals(14, a.getMaxWhenElapsed()); + for (int i = 0; i < NUM_POLICIES; i++) { + if (i == REQUESTER_POLICY_INDEX) { + continue; + } + a.setPolicyElapsed(i, 17); + // getWhenElapsed is 17, so getMaxWhenElapsed will return 17 too. + assertEquals(17, a.getMaxWhenElapsed()); + + a.setPolicyElapsed(i, 5); + assertEquals(14, a.getMaxWhenElapsed()); + } } @Test - public void setPolicyElapsed() { + public void setPolicyElapsedExact() { final Alarm exactAlarm = createDefaultAlarm(10, 0); assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4)); @@ -108,6 +139,10 @@ public class AlarmTest { assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7)); + } + + @Test + public void setPolicyElapsedInexact() { final Alarm inexactAlarm = createDefaultAlarm(10, 5); assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4)); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index c10b7b98bb5b..6a6fb82897bb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -429,7 +429,12 @@ public class LocalDisplayAdapterTest { public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0], 1000, 1000, 0); public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = - new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f); + new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0, + /* allowGroupSwitching */ false, + /* primaryRefreshRateMin */ 60.f, + /* primaryRefreshRateMax */ 60.f, + /* appRefreshRateMin */ 60.f, + /* appRefreshRateMax */60.f); private FakeDisplay(int port) { this.address = createDisplayAddress(port); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index b98021bc2cab..79542084ac36 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -307,7 +307,9 @@ public class QuotaControllerTest { private void trackJobs(JobStatus... jobs) { for (JobStatus job : jobs) { mJobStore.add(job); - mQuotaController.maybeStartTrackingJobLocked(job, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + } } } @@ -336,15 +338,19 @@ public class QuotaControllerTest { } private void setDeviceConfigLong(String key, long val) { - mQuotaController.prepareForUpdatedConstantsLocked(); mDeviceConfigPropertiesBuilder.setLong(key, val); - mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForUpdatedConstantsLocked(); + mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + } } private void setDeviceConfigInt(String key, int val) { - mQuotaController.prepareForUpdatedConstantsLocked(); mDeviceConfigPropertiesBuilder.setInt(key, val); - mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForUpdatedConstantsLocked(); + mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + } } @Test @@ -396,7 +402,9 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test", two); mQuotaController.saveTimingSession(0, "com.android.test", one); - mQuotaController.deleteObsoleteSessionsLocked(); + synchronized (mQuotaController.mLock) { + mQuotaController.deleteObsoleteSessionsLocked(); + } assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test")); } @@ -430,15 +438,21 @@ public class QuotaControllerTest { expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; final int uid = 10001; - mQuotaController.onAppRemovedLocked("com.android.test.remove", uid); + synchronized (mQuotaController.mLock) { + mQuotaController.onAppRemovedLocked("com.android.test.remove", uid); + } assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove")); assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay")); - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test.remove", RARE_INDEX)); - assertNotEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test.stay", RARE_INDEX)); - - assertFalse(mQuotaController.getForegroundUids().get(uid)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked( + 0, "com.android.test.remove", RARE_INDEX)); + assertNotEquals(expectedStats, + mQuotaController.getExecutionStatsLocked( + 0, "com.android.test.stay", RARE_INDEX)); + + assertFalse(mQuotaController.getForegroundUids().get(uid)); + } } @Test @@ -469,13 +483,15 @@ public class QuotaControllerTest { expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; - mQuotaController.onUserRemovedLocked(0); - assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); - assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test")); - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); - assertNotEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(10, "com.android.test", RARE_INDEX)); + synchronized (mQuotaController.mLock) { + mQuotaController.onUserRemovedLocked(0); + assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); + assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test")); + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); + assertNotEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(10, "com.android.test", RARE_INDEX)); + } } @Test @@ -504,7 +520,9 @@ public class QuotaControllerTest { inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100; // Invalid time is now +24 hours since there are no sessions at all for the app. expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS; @@ -516,7 +534,9 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 0; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS; @@ -527,7 +547,9 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 1; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * MINUTE_IN_MILLIS; @@ -539,7 +561,9 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 1; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS; @@ -551,7 +575,9 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 1; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS; @@ -562,7 +588,9 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 2; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS; @@ -576,7 +604,9 @@ public class QuotaControllerTest { expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 3; expectedStats.inQuotaTimeElapsed = now + 11 * MINUTE_IN_MILLIS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS; @@ -590,7 +620,9 @@ public class QuotaControllerTest { expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 4; expectedStats.inQuotaTimeElapsed = now + 5 * MINUTE_IN_MILLIS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * HOUR_IN_MILLIS; @@ -605,7 +637,9 @@ public class QuotaControllerTest { // App goes under job execution time limit in ~61 minutes, but will be under job count limit // in 65 minutes. expectedStats.inQuotaTimeElapsed = now + 65 * MINUTE_IN_MILLIS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS; @@ -618,7 +652,9 @@ public class QuotaControllerTest { expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.sessionCountInWindow = 5; expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS + 5 * MINUTE_IN_MILLIS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); // Make sure expirationTimeElapsed is set correctly when it's dependent on the max period. @@ -637,7 +673,9 @@ public class QuotaControllerTest { expectedStats.sessionCountInWindow = 5; expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); mQuotaController.getTimingSessions(0, "com.android.test") @@ -654,7 +692,9 @@ public class QuotaControllerTest { expectedStats.sessionCountInWindow = 5; expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; - mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); + } assertEquals(expectedStats, inputStats); } @@ -675,18 +715,23 @@ public class QuotaControllerTest { for (int i = 1; i < mQcConstants.MAX_JOB_COUNT_RARE; ++i) { JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", i); setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(7000); expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis(); expectedStats.executionTimeInWindowMs = expectedStats.executionTimeInMaxPeriodMs = 7000 * i; expectedStats.bgJobCountInWindow = expectedStats.bgJobCountInMaxPeriod = i; - mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); - assertEquals(expectedStats, inputStats); - assertTrue(mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - RARE_INDEX)); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); + assertEquals(expectedStats, inputStats); + assertTrue(mQuotaController.isWithinQuotaLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); + } assertTrue("Job not ready: " + jobStatus, jobStatus.isReady()); } @@ -704,16 +749,21 @@ public class QuotaControllerTest { // Active timer is under quota, so out of quota due to old session. expectedStats.inQuotaTimeElapsed = sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS; - mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); - assertEquals(expectedStats, inputStats); - assertFalse( - mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); + assertEquals(expectedStats, inputStats); + assertFalse( + mQuotaController.isWithinQuotaLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); + } // Quota should be exceeded due to activity in active timer. JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", 0); setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(10000); expectedStats.executionTimeInWindowMs += 10000; @@ -724,11 +774,14 @@ public class QuotaControllerTest { // time has passed since active timer. expectedStats.inQuotaTimeElapsed = sElapsedRealtimeClock.millis() + expectedStats.windowSizeMs; - mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); - assertEquals(expectedStats, inputStats); - assertFalse( - mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); - assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady()); + synchronized (mQuotaController.mLock) { + mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); + assertEquals(expectedStats, inputStats); + assertFalse( + mQuotaController.isWithinQuotaLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); + assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady()); + } } /** @@ -758,8 +811,10 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 20; expectedStats.sessionCountInWindow = 1; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX)); + } // Working expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS; @@ -773,8 +828,10 @@ public class QuotaControllerTest { expectedStats.sessionCountInWindow = 2; expectedStats.inQuotaTimeElapsed = now + 3 * MINUTE_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); + } // Frequent expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS; @@ -788,8 +845,11 @@ public class QuotaControllerTest { expectedStats.sessionCountInWindow = 3; expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX)); + } // Rare expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; @@ -803,8 +863,10 @@ public class QuotaControllerTest { expectedStats.sessionCountInWindow = 4; expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); + } } /** @@ -831,32 +893,41 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 2; expectedStats.sessionCountInWindow = 1; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX)); + } // Working expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); + } // Frequent expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; expectedStats.expirationTimeElapsed = 8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX)); + } // Rare expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; - assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); + } } /** @@ -894,105 +965,121 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(32, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(128, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(160, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(32, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(128, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(160, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(22, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(88, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(110, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(22, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(88, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(110, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(22, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(88, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(110, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(22, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(88, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(110, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 5 * SECOND_IN_MILLIS); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(14, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(56, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(70, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(14, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(56, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(70, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, MINUTE_IN_MILLIS); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(4, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(16, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(20, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(4, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(16, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(20, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 5 * MINUTE_IN_MILLIS); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(2, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(8, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(10, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(2, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(8, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(10, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 15 * MINUTE_IN_MILLIS); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(2, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(8, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(10, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(2, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(8, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(10, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference // between an hour and 15 minutes. setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS); - mQuotaController.invalidateAllExecutionStatsLocked(); - assertEquals(0, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); - assertEquals(2, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); - assertEquals(8, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); - assertEquals(10, mQuotaController.getExecutionStatsLocked( - 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(2, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(8, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(10, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } } /** @@ -1012,14 +1099,20 @@ public class QuotaControllerTest { createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5)); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); - final ExecutionStats originalStatsActive = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", ACTIVE_INDEX); - final ExecutionStats originalStatsWorking = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", WORKING_INDEX); - final ExecutionStats originalStatsFrequent = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", FREQUENT_INDEX); - final ExecutionStats originalStatsRare = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", RARE_INDEX); + final ExecutionStats originalStatsActive; + final ExecutionStats originalStatsWorking; + final ExecutionStats originalStatsFrequent; + final ExecutionStats originalStatsRare; + synchronized (mQuotaController.mLock) { + originalStatsActive = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX); + originalStatsWorking = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX); + originalStatsFrequent = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX); + originalStatsRare = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX); + } // Advance clock so that the working stats shouldn't be the same. advanceElapsedClock(MINUTE_IN_MILLIS); @@ -1037,8 +1130,11 @@ public class QuotaControllerTest { expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod; expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow; expectedStats.inQuotaTimeElapsed = originalStatsActive.inQuotaTimeElapsed; - final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", ACTIVE_INDEX); + final ExecutionStats newStatsActive; + synchronized (mQuotaController.mLock) { + newStatsActive = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX); + } // Stats for the same bucket should use the same object. assertTrue(originalStatsActive == newStatsActive); assertEquals(expectedStats, newStatsActive); @@ -1051,8 +1147,11 @@ public class QuotaControllerTest { expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow; expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow; expectedStats.inQuotaTimeElapsed = originalStatsWorking.inQuotaTimeElapsed; - final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", WORKING_INDEX); + final ExecutionStats newStatsWorking; + synchronized (mQuotaController.mLock) { + newStatsWorking = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX); + } assertTrue(originalStatsWorking == newStatsWorking); assertNotEquals(expectedStats, newStatsWorking); @@ -1064,8 +1163,11 @@ public class QuotaControllerTest { expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow; expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow; expectedStats.inQuotaTimeElapsed = originalStatsFrequent.inQuotaTimeElapsed; - final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", FREQUENT_INDEX); + final ExecutionStats newStatsFrequent; + synchronized (mQuotaController.mLock) { + newStatsFrequent = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX); + } assertTrue(originalStatsFrequent == newStatsFrequent); assertNotEquals(expectedStats, newStatsFrequent); @@ -1077,8 +1179,11 @@ public class QuotaControllerTest { expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow; expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow; expectedStats.inQuotaTimeElapsed = originalStatsRare.inQuotaTimeElapsed; - final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0, - "com.android.test", RARE_INDEX); + final ExecutionStats newStatsRare; + synchronized (mQuotaController.mLock) { + newStatsRare = mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX); + } assertTrue(originalStatsRare == newStatsRare); assertEquals(expectedStats, newStatsRare); } @@ -1092,26 +1197,36 @@ public class QuotaControllerTest { job.setStandbyBucket(RARE_INDEX); setCharging(); - assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, - mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + synchronized (mQuotaController.mLock) { + assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + } setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); - assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, - mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + synchronized (mQuotaController.mLock) { + assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + } // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); - mQuotaController.maybeStartTrackingJobLocked(job, null); - mQuotaController.prepareForExecutionLocked(job); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); - assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, - mQuotaController.getMaxJobExecutionTimeMsLocked((job))); - mQuotaController.maybeStopTrackingJobLocked(job, null, false); + synchronized (mQuotaController.mLock) { + assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); - assertEquals(7 * MINUTE_IN_MILLIS, - mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + synchronized (mQuotaController.mLock) { + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } } /** @@ -1137,30 +1252,46 @@ public class QuotaControllerTest { createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); setStandbyBucket(RARE_INDEX); - assertEquals(30 * SECOND_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } setStandbyBucket(FREQUENT_INDEX); - assertEquals(MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } setStandbyBucket(WORKING_INDEX); - assertEquals(5 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(7 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the // max execution time. setStandbyBucket(ACTIVE_INDEX); - assertEquals(7 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } } /** @@ -1175,11 +1306,15 @@ public class QuotaControllerTest { now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5)); setStandbyBucket(WORKING_INDEX); - assertEquals(8 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - // Max time will phase out, so should use bucket limit. - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(8 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + // Max time will phase out, so should use bucket limit. + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); // Close to boundary. @@ -1188,10 +1323,14 @@ public class QuotaControllerTest { 4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5)); setStandbyBucket(WORKING_INDEX); - assertEquals(5 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); // Far from boundary. @@ -1200,10 +1339,14 @@ public class QuotaControllerTest { now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5)); setStandbyBucket(WORKING_INDEX); - assertEquals(3 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(3 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(3 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(3 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } } /** @@ -1225,13 +1368,17 @@ public class QuotaControllerTest { createTimingSession( now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); - // Both max and bucket time have 8 minutes left. - assertEquals(8 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - // Max time essentially free. Bucket time has 2 min phase out plus original 8 minute - // window time. - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + // Both max and bucket time have 8 minutes left. + assertEquals(8 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + // Max time essentially free. Bucket time has 2 min phase out plus original 8 minute + // window time. + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); // Overlap boundary. @@ -1247,23 +1394,32 @@ public class QuotaControllerTest { createTimingSession( now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); - // Both max and bucket time have 8 minutes left. - assertEquals(8 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - // Max time only has one minute phase out. Bucket time has 2 minute phase out. - assertEquals(9 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + // Both max and bucket time have 8 minutes left. + assertEquals(8 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + // Max time only has one minute phase out. Bucket time has 2 minute phase out. + assertEquals(9 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } } @Test public void testIsWithinQuotaLocked_NeverApp() { - assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX)); + synchronized (mQuotaController.mLock) { + assertFalse( + mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX)); + } } @Test public void testIsWithinQuotaLocked_Charging() { setCharging(); - assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX)); + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX)); + } } @Test @@ -1275,7 +1431,9 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); mQuotaController.incrementJobCount(0, "com.android.test", 5); - assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + } } @Test @@ -1288,15 +1446,19 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test.spam", createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount)); mQuotaController.incrementJobCount(0, "com.android.test.spam", jobCount); - assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.spam", - WORKING_INDEX)); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinQuotaLocked( + 0, "com.android.test.spam", WORKING_INDEX)); + } mQuotaController.saveTimingSession(0, "com.android.test.frequent", createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000)); mQuotaController.saveTimingSession(0, "com.android.test.frequent", createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500)); - assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.frequent", - FREQUENT_INDEX)); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinQuotaLocked( + 0, "com.android.test.frequent", FREQUENT_INDEX)); + } } @Test @@ -1310,7 +1472,9 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5)); mQuotaController.incrementJobCount(0, "com.android.test", 5); - assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + } } @Test @@ -1323,7 +1487,9 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount)); mQuotaController.incrementJobCount(0, "com.android.test", jobCount); - assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + } } @Test @@ -1335,32 +1501,42 @@ public class QuotaControllerTest { setStandbyBucket(ACTIVE_INDEX, jobStatus); setProcessState(ActivityManager.PROCESS_STATE_BACKUP); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } for (int i = 0; i < 20; ++i) { advanceElapsedClock(SECOND_IN_MILLIS); setProcessState(ActivityManager.PROCESS_STATE_SERVICE); setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); } - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } advanceElapsedClock(15 * SECOND_IN_MILLIS); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } for (int i = 0; i < 20; ++i) { advanceElapsedClock(SECOND_IN_MILLIS); setProcessState(ActivityManager.PROCESS_STATE_SERVICE); setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); } - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS); - assertEquals(2, mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow); - assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus)); - assertTrue(jobStatus.isReady()); + synchronized (mQuotaController.mLock) { + assertEquals(2, mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow); + assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus)); + assertTrue(jobStatus.isReady()); + } } @Test @@ -1397,44 +1573,54 @@ public class QuotaControllerTest { doReturn(new String[]{fgChangerPkgName}) .when(packageManager).getPackagesForUid(fgChangerUid); - mQuotaController.maybeStartTrackingJobLocked(unaffected, null); - mQuotaController.prepareForExecutionLocked(unaffected); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(unaffected, null); + mQuotaController.prepareForExecutionLocked(unaffected); - mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); - mQuotaController.prepareForExecutionLocked(fgStateChanger); + mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); + mQuotaController.prepareForExecutionLocked(fgStateChanger); + } for (int i = 0; i < 20; ++i) { advanceElapsedClock(SECOND_IN_MILLIS); setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid); setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid); } - mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false); + } advanceElapsedClock(15 * SECOND_IN_MILLIS); - mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); - mQuotaController.prepareForExecutionLocked(fgStateChanger); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); + mQuotaController.prepareForExecutionLocked(fgStateChanger); + } for (int i = 0; i < 20; ++i) { advanceElapsedClock(SECOND_IN_MILLIS); setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid); setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid); } - mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false); - mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false); + mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false); - assertTrue(mQuotaController.isWithinQuotaLocked(unaffected)); - assertTrue(unaffected.isReady()); - assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger)); - assertFalse(fgStateChanger.isReady()); + assertTrue(mQuotaController.isWithinQuotaLocked(unaffected)); + assertTrue(unaffected.isReady()); + assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger)); + assertFalse(fgStateChanger.isReady()); + } assertEquals(1, mQuotaController.getTimingSessions(SOURCE_USER_ID, unaffectedPkgName).size()); assertEquals(42, mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size()); - for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) { - assertEquals(42, mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow); - assertEquals(1, mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow); + synchronized (mQuotaController.mLock) { + for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) { + assertEquals(42, mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow); + assertEquals(1, mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow); + } } } @@ -1453,25 +1639,30 @@ public class QuotaControllerTest { 2)); mQuotaController.incrementJobCount(0, "com.android.test", 2); - assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions", - i < 2, - mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX)); - assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions", - i < 3, - mQuotaController.isWithinQuotaLocked(0, "com.android.test", FREQUENT_INDEX)); - assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions", - i < 4, - mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); - assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions", - i < 5, - mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX)); + synchronized (mQuotaController.mLock) { + assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions", + i < 2, + mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX)); + assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions", + i < 3, + mQuotaController.isWithinQuotaLocked( + 0, "com.android.test", FREQUENT_INDEX)); + assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions", + i < 4, + mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); + assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions", + i < 5, + mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX)); + } } } @Test public void testMaybeScheduleCleanupAlarmLocked() { // No sessions saved yet. - mQuotaController.maybeScheduleCleanupAlarmLocked(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleCleanupAlarmLocked(); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any()); // Test with only one timing session saved. @@ -1479,7 +1670,9 @@ public class QuotaControllerTest { final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); mQuotaController.saveTimingSession(0, "com.android.test", new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1)); - mQuotaController.maybeScheduleCleanupAlarmLocked(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleCleanupAlarmLocked(); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any()); @@ -1488,7 +1681,9 @@ public class QuotaControllerTest { createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleCleanupAlarmLocked(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleCleanupAlarmLocked(); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any()); } @@ -1505,8 +1700,10 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); // No sessions saved yet. - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -1519,27 +1716,35 @@ public class QuotaControllerTest { createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1)); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1); setStandbyBucket(standbyBucket, jobStatus); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(5 * MINUTE_IN_MILLIS); - // Timer has only been going for 5 minutes in the past 10 minutes, which is under the window - // size limit, but the total execution time for the past 24 hours is 6 hours, so the job no - // longer has quota. - assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + // Timer has only been going for 5 minutes in the past 10 minutes, which is under the + // window size limit, but the total execution time for the past 24 hours is 6 hours, so + // the job no longer has quota. + assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, times(1)).set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -1554,15 +1759,19 @@ public class QuotaControllerTest { // Working set window size is 2 hours. final int standbyBucket = WORKING_INDEX; - // No sessions saved yet. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + // No sessions saved yet. + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test with timing sessions in window but still in quota. @@ -1572,7 +1781,9 @@ public class QuotaControllerTest { end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Add some more sessions, but still in quota. @@ -1580,18 +1791,24 @@ public class QuotaControllerTest { createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test when out of quota. mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); // Alarm already scheduled, so make sure it's not scheduled again. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -1607,14 +1824,18 @@ public class QuotaControllerTest { final int standbyBucket = FREQUENT_INDEX; // No sessions saved yet. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test with timing sessions in window but still in quota. @@ -1622,7 +1843,9 @@ public class QuotaControllerTest { final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Add some more sessions, but still in quota. @@ -1630,18 +1853,24 @@ public class QuotaControllerTest { createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test when out of quota. mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); // Alarm already scheduled, so make sure it's not scheduled again. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -1660,14 +1889,18 @@ public class QuotaControllerTest { setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 50); // No sessions saved yet. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test with timing sessions in window but still in quota. @@ -1679,7 +1912,9 @@ public class QuotaControllerTest { + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Add some more sessions, but still in quota. @@ -1687,18 +1922,24 @@ public class QuotaControllerTest { createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1)); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Test when out of quota. mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1)); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); // Alarm already scheduled, so make sure it's not scheduled again. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -1730,7 +1971,9 @@ public class QuotaControllerTest { InOrder inOrder = inOrder(mAlarmManager); // Start in ACTIVE bucket. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX); + } inOrder.verify(mAlarmManager, never()) .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class)); @@ -1739,34 +1982,46 @@ public class QuotaControllerTest { final long expectedWorkingAlarmTime = outOfQuotaTime + (2 * HOUR_IN_MILLIS) + mQcConstants.IN_QUOTA_BUFFER_MS; - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); + } inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS) + mQcConstants.IN_QUOTA_BUFFER_MS; - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); + } inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS) + mQcConstants.IN_QUOTA_BUFFER_MS; - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX); + } inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); // And back up again. - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); + } inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); + } inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); - mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX); + } inOrder.verify(mAlarmManager, never()) .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class)); @@ -1781,22 +2036,29 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int standbyBucket = WORKING_INDEX; - ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, - SOURCE_PACKAGE, standbyBucket); + ExecutionStats stats; + synchronized (mQuotaController.mLock) { + stats = mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } stats.jobCountInRateLimitingWindow = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW + 2; // Invalid time in the past, so the count shouldn't be used. stats.jobRateLimitExpirationTimeElapsed = now - 5 * MINUTE_IN_MILLIS / 2; - mQuotaController.maybeScheduleStartAlarmLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Valid time in the future, so the count should be used. stats.jobRateLimitExpirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS / 2; final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed; - mQuotaController.maybeScheduleStartAlarmLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -1884,8 +2146,10 @@ public class QuotaControllerTest { // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -1915,8 +2179,10 @@ public class QuotaControllerTest { final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS + 24 * HOUR_IN_MILLIS + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } @@ -2070,13 +2336,19 @@ public class QuotaControllerTest { setCharging(); JobStatus jobStatus = createJobStatus("testTimerTracking_Charging", 1); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(5 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2087,41 +2359,63 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_BACKUP); JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); List<TimingSession> expected = new ArrayList<>(); long start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(5 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); // Test overlapping jobs. JobStatus jobStatus2 = createJobStatus("testTimerTracking_Discharging", 2); - mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + } JobStatus jobStatus3 = createJobStatus("testTimerTracking_Discharging", 3); - mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + } advanceElapsedClock(SECOND_IN_MILLIS); start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobStatus2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobStatus3); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus3); + } advanceElapsedClock(20 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2135,11 +2429,17 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2); - mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + } JobStatus jobStatus3 = createJobStatus("testTimerTracking_ChargingAndDischarging", 3); - mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); List<TimingSession> expected = new ArrayList<>(); @@ -2147,12 +2447,16 @@ public class QuotaControllerTest { // should be counted. setCharging(); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); setDischarging(); long start = JobSchedulerService.sElapsedRealtimeClock.millis(); advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true); + } expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); @@ -2166,23 +2470,35 @@ public class QuotaControllerTest { // shouldn't be included in either job count. setDischarging(); start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobStatus2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); setCharging(); expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); - mQuotaController.prepareForExecutionLocked(jobStatus3); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus3); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); setDischarging(); start = JobSchedulerService.sElapsedRealtimeClock.millis(); advanceElapsedClock(20 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); @@ -2190,13 +2506,17 @@ public class QuotaControllerTest { // during the discharging period should be counted. setDischarging(); start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); - mQuotaController.prepareForExecutionLocked(jobStatus2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + mQuotaController.prepareForExecutionLocked(jobStatus2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); setCharging(); advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2207,42 +2527,63 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); JobStatus jobStatus = createJobStatus("testTimerTracking_AllBackground", 1); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); List<TimingSession> expected = new ArrayList<>(); // Test single job. long start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(5 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); // Test overlapping jobs. JobStatus jobStatus2 = createJobStatus("testTimerTracking_AllBackground", 2); - mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); + } JobStatus jobStatus3 = createJobStatus("testTimerTracking_AllBackground", 3); - mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); + } advanceElapsedClock(SECOND_IN_MILLIS); start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobStatus2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobStatus3); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus3); + } advanceElapsedClock(20 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false); + } expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2254,16 +2595,22 @@ public class QuotaControllerTest { JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1); setProcessState(ActivityManager.PROCESS_STATE_TOP); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(5 * SECOND_IN_MILLIS); // Change to a state that should still be considered foreground. setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); advanceElapsedClock(5 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2278,18 +2625,24 @@ public class QuotaControllerTest { JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1); JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2); JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3); - mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); List<TimingSession> expected = new ArrayList<>(); // UID starts out inactive. setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); long start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.prepareForExecutionLocked(jobBg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); @@ -2301,16 +2654,24 @@ public class QuotaControllerTest { // App remains in foreground state after coming to foreground, so there should only be one // session. start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - mQuotaController.prepareForExecutionLocked(jobBg2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.prepareForExecutionLocked(jobBg2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); - mQuotaController.prepareForExecutionLocked(jobFg3); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg3); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); advanceElapsedClock(SECOND_IN_MILLIS); @@ -2321,25 +2682,39 @@ public class QuotaControllerTest { // * The first should have a count of 1 // * The second should have a count of 2 since it will include both jobs start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); + } setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); - mQuotaController.prepareForExecutionLocked(jobBg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); - mQuotaController.prepareForExecutionLocked(jobFg3); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg3); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now start = JobSchedulerService.sElapsedRealtimeClock.millis(); setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); - mQuotaController.prepareForExecutionLocked(jobBg2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2355,21 +2730,34 @@ public class QuotaControllerTest { JobStatus jobFg1 = createJobStatus("testTimerTracking_JobCount_Foreground", 1); JobStatus jobFg2 = createJobStatus("testTimerTracking_JobCount_Foreground", 2); - mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobFg2, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg2, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); - ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, - SOURCE_PACKAGE, standbyBucket); + ExecutionStats stats; + synchronized (mQuotaController.mLock) { + stats = mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } assertEquals(0, stats.jobCountInRateLimitingWindow); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); - mQuotaController.prepareForExecutionLocked(jobFg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg1); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobFg2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); assertEquals(0, stats.jobCountInRateLimitingWindow); @@ -2383,21 +2771,32 @@ public class QuotaControllerTest { final int standbyBucket = WORKING_INDEX; JobStatus jobBg1 = createJobStatus("testTimerTracking_JobCount_Background", 1); JobStatus jobBg2 = createJobStatus("testTimerTracking_JobCount_Background", 2); - mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + ExecutionStats stats; + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, - SOURCE_PACKAGE, standbyBucket); + stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, + SOURCE_PACKAGE, standbyBucket); + } assertEquals(0, stats.jobCountInRateLimitingWindow); setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); - mQuotaController.prepareForExecutionLocked(jobBg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.prepareForExecutionLocked(jobBg2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } assertEquals(2, stats.jobCountInRateLimitingWindow); } @@ -2413,19 +2812,25 @@ public class QuotaControllerTest { JobStatus jobBg2 = createJobStatus("testTimerTracking_TopAndNonTop", 2); JobStatus jobFg1 = createJobStatus("testTimerTracking_TopAndNonTop", 3); JobStatus jobTop = createJobStatus("testTimerTracking_TopAndNonTop", 4); - mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + } assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); List<TimingSession> expected = new ArrayList<>(); // UID starts out inactive. setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); long start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.prepareForExecutionLocked(jobBg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); @@ -2437,16 +2842,24 @@ public class QuotaControllerTest { // App remains in top state after coming to top, so there should only be one // session. start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - mQuotaController.prepareForExecutionLocked(jobBg2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.prepareForExecutionLocked(jobBg2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); setProcessState(ActivityManager.PROCESS_STATE_TOP); - mQuotaController.prepareForExecutionLocked(jobTop); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + } assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); advanceElapsedClock(SECOND_IN_MILLIS); @@ -2459,31 +2872,47 @@ public class QuotaControllerTest { // * The second should have a count of 2, which accounts for the bg2 and fg, but not top // jobs. start = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); - mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); - mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + } setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); - mQuotaController.prepareForExecutionLocked(jobBg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); setProcessState(ActivityManager.PROCESS_STATE_TOP); - mQuotaController.prepareForExecutionLocked(jobTop); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true); + } advanceElapsedClock(5 * SECOND_IN_MILLIS); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); - mQuotaController.prepareForExecutionLocked(jobFg1); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg1); + } advanceElapsedClock(5 * SECOND_IN_MILLIS); setProcessState(ActivityManager.PROCESS_STATE_TOP); advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now start = JobSchedulerService.sElapsedRealtimeClock.millis(); setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); - mQuotaController.prepareForExecutionLocked(jobBg2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false); + } advanceElapsedClock(10 * SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); - mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false); + mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false); + } expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2511,20 +2940,28 @@ public class QuotaControllerTest { // UID starts out inactive. setProcessState(ActivityManager.PROCESS_STATE_SERVICE); // Start the job. - mQuotaController.prepareForExecutionLocked(jobBg); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg); + } advanceElapsedClock(remainingTimeMs / 2); // New job starts after UID is in the foreground. Since the app is now in the foreground, it // should continue to have remainingTimeMs / 2 time remaining. setProcessState(ActivityManager.PROCESS_STATE_TOP); - mQuotaController.prepareForExecutionLocked(jobTop); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } advanceElapsedClock(remainingTimeMs); // Wait for some extra time to allow for job processing. inOrder.verify(mJobSchedulerService, timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) .onControllerStateChanged(); - assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobBg)); - assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobTop)); + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs / 2, + mQuotaController.getRemainingExecutionTimeLocked(jobBg)); + assertEquals(remainingTimeMs / 2, + mQuotaController.getRemainingExecutionTimeLocked(jobTop)); + } // Go to a background state. setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); advanceElapsedClock(remainingTimeMs / 2 + 1); @@ -2546,7 +2983,9 @@ public class QuotaControllerTest { inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) .onControllerStateChanged(); trackJobs(jobFg, jobTop); - mQuotaController.prepareForExecutionLocked(jobTop); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); @@ -2578,7 +3017,9 @@ public class QuotaControllerTest { @Test public void testTracking_OutOfQuota() { JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window setProcessState(ActivityManager.PROCESS_STATE_HOME); // Now the package only has two seconds to run. @@ -2589,7 +3030,9 @@ public class QuotaControllerTest { 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1)); // Start the job. - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(remainingTimeMs); // Wait for some extra time to allow for job processing. @@ -2608,7 +3051,9 @@ public class QuotaControllerTest { @Test public void testTracking_RollingQuota() { JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1); - mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + } setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window setProcessState(ActivityManager.PROCESS_STATE_SERVICE); Handler handler = mQuotaController.getHandler(); @@ -2625,9 +3070,13 @@ public class QuotaControllerTest { createTimingSession(now - HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1)); - assertEquals(remainingTimeMs, mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); - // Start the job. - mQuotaController.prepareForExecutionLocked(jobStatus); + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs, + mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); + + // Start the job. + mQuotaController.prepareForExecutionLocked(jobStatus); + } advanceElapsedClock(remainingTimeMs); // Wait for some extra time to allow for job processing. @@ -2638,8 +3087,11 @@ public class QuotaControllerTest { // The job used up the remaining quota, but in that time, the same amount of time in the // old TimingSession also fell out of the quota window, so it should still have the same // amount of remaining time left its quota. - assertEquals(remainingTimeMs, - mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } // Handler is told to check when the quota will be consumed, not when the initial // remaining time is over. verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(10 * SECOND_IN_MILLIS)); @@ -2675,19 +3127,25 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); // No sessions saved yet. - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Ran jobs up to the job limit. All of them should be allowed to run. for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; ++i) { JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i); setStandbyBucket(WORKING_INDEX, job); - mQuotaController.maybeStartTrackingJobLocked(job, null); - assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - mQuotaController.prepareForExecutionLocked(job); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + mQuotaController.prepareForExecutionLocked(job); + } advanceElapsedClock(SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(job, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } advanceElapsedClock(SECOND_IN_MILLIS); } // Start alarm shouldn't have been scheduled since the app was in quota up until this point. @@ -2697,11 +3155,16 @@ public class QuotaControllerTest { JobStatus throttledJob = createJobStatus( "testStartAlarmScheduled_JobCount_AllowedTime", 42); setStandbyBucket(WORKING_INDEX, throttledJob); - mQuotaController.maybeStartTrackingJobLocked(throttledJob, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(throttledJob, null); + } assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, - SOURCE_PACKAGE, standbyBucket); + ExecutionStats stats; + synchronized (mQuotaController.mLock) { + stats = mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed; verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); @@ -2730,8 +3193,10 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); // No sessions saved yet. - mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, - standbyBucket); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeScheduleStartAlarmLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Ran jobs up to the job limit. All of them should be allowed to run. @@ -2739,12 +3204,16 @@ public class QuotaControllerTest { JobStatus job = createJobStatus( "testStartAlarmScheduled_TimingSessionCount_AllowedTime", i); setStandbyBucket(FREQUENT_INDEX, job); - mQuotaController.maybeStartTrackingJobLocked(job, null); - assertTrue("Constraint not satisfied for job #" + (i + 1), - job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - mQuotaController.prepareForExecutionLocked(job); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + assertTrue("Constraint not satisfied for job #" + (i + 1), + job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + mQuotaController.prepareForExecutionLocked(job); + } advanceElapsedClock(SECOND_IN_MILLIS); - mQuotaController.maybeStopTrackingJobLocked(job, null, false); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } advanceElapsedClock(SECOND_IN_MILLIS); } // Start alarm shouldn't have been scheduled since the app was in quota up until this point. @@ -2753,13 +3222,18 @@ public class QuotaControllerTest { // The app is now out of session count quota JobStatus throttledJob = createJobStatus( "testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42); - mQuotaController.maybeStartTrackingJobLocked(throttledJob, null); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(throttledJob, null); + } assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(), throttledJob.getWhenStandbyDeferred()); - ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, - SOURCE_PACKAGE, standbyBucket); + ExecutionStats stats; + synchronized (mQuotaController.mLock) { + stats = mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); + } final long expectedWorkingAlarmTime = stats.sessionRateLimitExpirationTimeElapsed; verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java index 3614763fecab..9d847153661f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java @@ -52,6 +52,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoSession; @@ -632,6 +633,50 @@ public class TimeControllerTest { } @Test + public void testDelayAlarmSchedulingCoalescedIntervals() { + doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); + + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + JobStatus jobLatest = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals", + createJob().setMinimumLatency(HOUR_IN_MILLIS)); + JobStatus jobMiddle = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals", + createJob().setMinimumLatency(TimeController.DELAY_COALESCE_TIME_MS / 2)); + JobStatus jobEarliest = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals", + createJob().setMinimumLatency(TimeController.DELAY_COALESCE_TIME_MS / 10)); + + ArgumentCaptor<AlarmManager.OnAlarmListener> listenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + InOrder inOrder = inOrder(mAlarmManager); + + mTimeController.maybeStartTrackingJobLocked(jobEarliest, null); + mTimeController.maybeStartTrackingJobLocked(jobMiddle, null); + mTimeController.maybeStartTrackingJobLocked(jobLatest, null); + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(now + TimeController.DELAY_COALESCE_TIME_MS / 10), anyLong(), + anyLong(), eq(TAG_DELAY), + listenerCaptor.capture(), any(), any()); + final AlarmManager.OnAlarmListener delayListener = listenerCaptor.getValue(); + + advanceElapsedClock(TimeController.DELAY_COALESCE_TIME_MS / 10); + delayListener.onAlarm(); + // The next delay alarm time should be TimeController.DELAY_COALESCE_TIME_MS after the last + // time the delay alarm fired. + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(now + TimeController.DELAY_COALESCE_TIME_MS / 10 + + TimeController.DELAY_COALESCE_TIME_MS), anyLong(), + anyLong(), eq(TAG_DELAY), any(), any(), any()); + + advanceElapsedClock(TimeController.DELAY_COALESCE_TIME_MS); + delayListener.onAlarm(); + // The last job is significantly after the coalesce time, so the 3rd scheduling shouldn't be + // affected by the first two jobs' alarms. + inOrder.verify(mAlarmManager, times(1)) + .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), + anyLong(), eq(TAG_DELAY), any(), any(), any()); + } + + @Test public void testEvaluateStateLocked_Delay() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index 3aedd3c7d753..d260e4dce79a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -22,6 +22,7 @@ import static android.app.AppOpsManager.OP_MONITOR_LOCATION; import static android.location.Criteria.ACCURACY_COARSE; import static android.location.Criteria.ACCURACY_FINE; import static android.location.Criteria.POWER_HIGH; +import static android.location.LocationRequest.PASSIVE_INTERVAL; import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; import static androidx.test.ext.truth.location.LocationSubject.assertThat; @@ -598,6 +599,38 @@ public class LocationProviderManagerTest { } @Test + public void testRegisterListener_Coarse() throws Exception { + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_COARSE, + listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, times(1)) + .onLocationChanged(any(Location.class), nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_Coarse_Passive() throws Exception { + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + new LocationRequest.Builder(PASSIVE_INTERVAL) + .setMinUpdateIntervalMillis(0) + .setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_COARSE, + listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, times(1)) + .onLocationChanged(any(Location.class), nullable(IRemoteCallback.class)); + } + + @Test public void testGetCurrentLocation() throws Exception { ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index 8d60de9abe99..fba321e07207 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -52,6 +52,8 @@ import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -76,7 +78,11 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; /** * Tests for {@link VibratorService}. @@ -444,6 +450,42 @@ public class VibratorServiceTest { } @Test + public void vibrate_withWaveform_totalVibrationTimeRespected() throws Exception { + int totalDuration = 10_000; // 10s + int stepDuration = 25; // 25ms + + // 25% of the first waveform step will be spent on the native on() call. + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + doAnswer(invocation -> { + Thread.currentThread().sleep(stepDuration / 4); + return null; + }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); + // 25% of each waveform step will be spent on the native setAmplitude() call.. + doAnswer(invocation -> { + Thread.currentThread().sleep(stepDuration / 4); + return null; + }).when(mNativeWrapperMock).vibratorSetAmplitude(anyInt()); + + VibratorService service = createService(); + + int stepCount = totalDuration / stepDuration; + long[] timings = new long[stepCount]; + int[] amplitudes = new int[stepCount]; + Arrays.fill(timings, stepDuration); + Arrays.fill(amplitudes, VibrationEffect.DEFAULT_AMPLITUDE); + VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1); + + int perceivedDuration = vibrateAndMeasure(service, effect, /* timeoutSecs= */ 15); + int delay = Math.abs(perceivedDuration - totalDuration); + + // Allow some delay for thread scheduling and callback triggering. + int maxDelay = (int) (0.05 * totalDuration); // < 5% of total duration + assertTrue("Waveform with perceived delay of " + delay + "ms," + + " expected less than " + maxDelay + "ms", + delay < maxDelay); + } + + @Test public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { VibratorService service = createService(); doAnswer(invocation -> { @@ -699,16 +741,41 @@ public class VibratorServiceTest { vibrate(service, effect, ALARM_ATTRS); } - private void vibrate(VibratorService service, VibrationEffect effect, AudioAttributes attrs) { - VibrationAttributes attributes = new VibrationAttributes.Builder(attrs, effect).build(); - vibrate(service, effect, attributes); - } - private void vibrate(VibratorService service, VibrationEffect effect, VibrationAttributes attributes) { service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service); } + private int vibrateAndMeasure( + VibratorService service, VibrationEffect effect, long timeoutSecs) throws Exception { + AtomicLong startTime = new AtomicLong(0); + AtomicLong endTime = new AtomicLong(0); + CountDownLatch startedCount = new CountDownLatch(1); + CountDownLatch finishedCount = new CountDownLatch(1); + service.registerVibratorStateListener(new IVibratorStateListener() { + @Override + public void onVibrating(boolean vibrating) throws RemoteException { + if (vibrating) { + startTime.set(SystemClock.uptimeMillis()); + startedCount.countDown(); + } else if (startedCount.getCount() == 0) { + endTime.set(SystemClock.uptimeMillis()); + finishedCount.countDown(); + } + } + + @Override + public IBinder asBinder() { + return mVibratorStateListenerBinderMock; + } + }); + + vibrate(service, effect); + + assertTrue(finishedCount.await(timeoutSecs, TimeUnit.SECONDS)); + return (int) (endTime.get() - startTime.get()); + } + private void mockVibratorCapabilities(int capabilities) { when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities); } diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java index 377bfd1170a6..24f8eabdf545 100644 --- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java @@ -18,17 +18,25 @@ package com.android.server.am; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.os.Handler; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import com.android.server.LocalServices; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerService; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; @@ -42,6 +50,18 @@ import java.io.File; @FlakyTest(bugId = 113616538) public class AppErrorDialogTest { + @BeforeClass + public static void setUpOnce() { + final PackageManagerInternal pm = mock(PackageManagerInternal.class); + doReturn(new ComponentName("", "")).when(pm).getSystemUiServiceComponent(); + LocalServices.addService(PackageManagerInternal.class, pm); + } + + @AfterClass + public static void tearDownOnce() { + LocalServices.removeServiceForTest(PackageManagerInternal.class); + } + @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index b8dbd6267bc5..325ba118c711 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; @@ -33,8 +35,6 @@ import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; -import com.google.common.truth.Truth; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -90,9 +90,9 @@ public class DisplayModeDirectorTest { // With no votes present, DisplayModeDirector should allow any refresh rate. DesiredDisplayModeSpecs modeSpecs = createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60); - Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f); - Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(modeSpecs.baseModeId).isEqualTo(60); + assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f); + assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY); int numPriorities = DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1; @@ -112,10 +112,10 @@ public class DisplayModeDirectorTest { votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i)); director.injectVotesByDisplay(votesByDisplay); modeSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i); - Truth.assertThat(modeSpecs.primaryRefreshRateRange.min) + assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i); + assertThat(modeSpecs.primaryRefreshRateRange.min) .isEqualTo((float) (minFps + i)); - Truth.assertThat(modeSpecs.primaryRefreshRateRange.max) + assertThat(modeSpecs.primaryRefreshRateRange.max) .isEqualTo((float) (maxFps - i)); } } @@ -132,9 +132,9 @@ public class DisplayModeDirectorTest { votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80)); director.injectVotesByDisplay(votesByDisplay); modeSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(modeSpecs.baseModeId).isEqualTo(70); - Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f); - Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f); + assertThat(modeSpecs.baseModeId).isEqualTo(70); + assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f); + assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f); } } @@ -153,9 +153,9 @@ public class DisplayModeDirectorTest { director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(60); } @Test @@ -172,32 +172,32 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60)); votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); } @Test @@ -219,29 +219,29 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); - Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); - Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(75, 75)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); - Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min) + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.appRequestRefreshRateRange.min) .isWithin(FLOAT_TOLERANCE) .of(75); - Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max) + assertThat(desiredSpecs.appRequestRefreshRateRange.max) .isWithin(FLOAT_TOLERANCE) .of(75); } @@ -251,10 +251,10 @@ public class DisplayModeDirectorTest { float appRequestMin, float appRequestMax) { DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings( minFps, peakFps, defaultFps); - Truth.assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin); - Truth.assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax); - Truth.assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin); - Truth.assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax); + assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin); + assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax); + assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin); + assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax); } @Test @@ -318,26 +318,84 @@ public class DisplayModeDirectorTest { director.injectVotesByDisplay(votesByDisplay); - Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); + assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(60); director.setShouldAlwaysRespectAppRequestedMode(true); - Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue(); + assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue(); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); - Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(90); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.baseModeId).isEqualTo(90); director.setShouldAlwaysRespectAppRequestedMode(false); - Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); + assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); + + desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(60); + } + + @Test + public void testVotingWithSwitchingTypeNone() { + final int displayId = 0; + DisplayModeDirector director = createDirectorFromFpsRange(0, 90); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(displayId, votes); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(30, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + + + director.injectVotesByDisplay(votesByDisplay); + assertThat(director.getModeSwitchingType()) + .isNotEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(30); + + director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_NONE); + assertThat(director.getModeSwitchingType()) + .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30); + assertThat(desiredSpecs.baseModeId).isEqualTo(30); + } + + @Test + public void testVotingWithSwitchingTypeWithinGroups() { + final int displayId = 0; + DisplayModeDirector director = createDirectorFromFpsRange(0, 90); + + director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS); + assertThat(director.getModeSwitchingType()) + .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + assertThat(desiredSpecs.allowGroupSwitching).isFalse(); + } + + @Test + public void testVotingWithSwitchingTypeWithinAndAcrossGroups() { + final int displayId = 0; + DisplayModeDirector director = createDirectorFromFpsRange(0, 90); + + director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); + assertThat(director.getModeSwitchingType()) + .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + assertThat(desiredSpecs.allowGroupSwitching).isTrue(); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index a1eb0378a210..ae9c6188619f 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -26,26 +26,16 @@ import android.hardware.hdmi.HdmiControlManager; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; import android.sysprop.HdmiProperties; -import android.util.Slog; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; -import com.android.server.hdmi.cec.config.CecSettings; -import com.android.server.hdmi.cec.config.XmlParser; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import javax.xml.datatype.DatatypeConfigurationException; @SmallTest @Presubmit @@ -65,13 +55,15 @@ public final class HdmiCecConfigTest { @Test public void getAllCecSettings_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, null, null); assertThat(hdmiCecConfig.getAllSettings()).isEmpty(); } @Test public void getAllCecSettings_Empty() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + "</cec-settings>", null); @@ -80,7 +72,8 @@ public final class HdmiCecConfigTest { @Test public void getAllCecSettings_BasicSanity() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"hdmi_cec_enabled\"" @@ -108,13 +101,15 @@ public final class HdmiCecConfigTest { @Test public void getUserCecSettings_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, null, null); assertThat(hdmiCecConfig.getUserSettings()).isEmpty(); } @Test public void getUserCecSettings_Empty() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + "</cec-settings>", null); @@ -123,7 +118,8 @@ public final class HdmiCecConfigTest { @Test public void getUserCecSettings_OnlyMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"hdmi_cec_enabled\"" @@ -151,7 +147,8 @@ public final class HdmiCecConfigTest { @Test public void getUserCecSettings_WithOverride() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"hdmi_cec_enabled\"" @@ -190,14 +187,16 @@ public final class HdmiCecConfigTest { @Test public void getAllowedValues_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, null, null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getAllowedValues("foo")); } @Test public void getAllowedValues_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + "</cec-settings>", null); @@ -207,7 +206,8 @@ public final class HdmiCecConfigTest { @Test public void getAllowedValues_BasicSanity() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"send_standby_on_sleep\"" @@ -229,14 +229,16 @@ public final class HdmiCecConfigTest { @Test public void getDefaultValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, null, null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getDefaultValue("foo")); } @Test public void getDefaultValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + "</cec-settings>", null); @@ -246,7 +248,8 @@ public final class HdmiCecConfigTest { @Test public void getDefaultValue_BasicSanity() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"send_standby_on_sleep\"" @@ -266,14 +269,16 @@ public final class HdmiCecConfigTest { @Test public void getValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, null, null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getValue("foo")); } @Test public void getValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + "</cec-settings>", null); @@ -287,7 +292,8 @@ public final class HdmiCecConfigTest { Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV)) .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"send_standby_on_sleep\"" @@ -313,7 +319,8 @@ public final class HdmiCecConfigTest { .NONE.name().toLowerCase())) .thenReturn(HdmiProperties.power_state_change_on_active_source_lost_values .STANDBY_NOW.name().toLowerCase()); - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"power_state_change_on_active_source_lost\"" @@ -333,14 +340,16 @@ public final class HdmiCecConfigTest { @Test public void setValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, null, null); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setValue("foo", "bar")); } @Test public void setValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + "</cec-settings>", null); @@ -350,7 +359,8 @@ public final class HdmiCecConfigTest { @Test public void setValue_NotConfigurable() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"send_standby_on_sleep\"" @@ -371,7 +381,8 @@ public final class HdmiCecConfigTest { @Test public void setValue_InvalidValue() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"send_standby_on_sleep\"" @@ -392,7 +403,8 @@ public final class HdmiCecConfigTest { @Test public void setValue_GlobalSetting_BasicSanity() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"send_standby_on_sleep\"" @@ -414,7 +426,8 @@ public final class HdmiCecConfigTest { @Test public void setValue_SystemProperty_BasicSanity() { - HdmiCecConfig hdmiCecConfig = createHdmiCecConfig( + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"power_state_change_on_active_source_lost\"" @@ -435,22 +448,4 @@ public final class HdmiCecConfigTest { HdmiProperties.power_state_change_on_active_source_lost_values .STANDBY_NOW.name().toLowerCase()); } - - private HdmiCecConfig createHdmiCecConfig(String productConfigXml, String vendorOverrideXml) { - CecSettings productConfig = null; - CecSettings vendorOverride = null; - try { - if (productConfigXml != null) { - productConfig = XmlParser.read( - new ByteArrayInputStream(productConfigXml.getBytes())); - } - if (vendorOverrideXml != null) { - vendorOverride = XmlParser.read( - new ByteArrayInputStream(vendorOverrideXml.getBytes())); - } - } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { - Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e); - } - return new HdmiCecConfig(mContext, mStorageAdapter, productConfig, vendorOverride); - } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java index 6e7ec2a88140..81c3be5f9f75 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java @@ -20,6 +20,8 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK; import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV; import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; +import static com.android.server.hdmi.Constants.ADDR_BACKUP_1; +import static com.android.server.hdmi.Constants.ADDR_BACKUP_2; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3; @@ -68,9 +70,15 @@ public class HdmiCecControllerTest { Looper getServiceLooper() { return mMyLooper; } + + @Override + int getCecVersion() { + return mCecVersion; + } } private HdmiCecController mHdmiCecController; + private int mCecVersion = Constants.VERSION_1_4; private int mLogicalAddress = 16; private AllocateAddressCallback mCallback = new AllocateAddressCallback() { @@ -191,4 +199,61 @@ public class HdmiCecControllerTest { mTestLooper.dispatchAll(); assertEquals(ADDR_UNREGISTERED, mLogicalAddress); } + + @Test + public void testAllocateLogicalAddress_PlaybackNonPreferred_2_0_BackupOne() { + mCecVersion = Constants.VERSION_2_0; + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS); + mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); + mTestLooper.dispatchAll(); + assertEquals(ADDR_BACKUP_1, mLogicalAddress); + } + + @Test + public void testAllocateLogicalAddress_PlaybackNonPreferred_2_0_BackupTwo() { + mCecVersion = Constants.VERSION_2_0; + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_BACKUP_1, SendMessageResult.SUCCESS); + mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); + mTestLooper.dispatchAll(); + assertEquals(ADDR_BACKUP_2, mLogicalAddress); + } + + @Test + public void testAllocateLogicalAddress_PlaybackPreferredOccupiedDedicatedBelowAvailable() { + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS); + mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_2, mCallback); + mTestLooper.dispatchAll(); + assertEquals(ADDR_PLAYBACK_1, mLogicalAddress); + } + + @Test + public void testAllocateLogicalAddress_PlaybackPreferredOccupiedDedicatedAboveAvailable() { + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); + mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_2, mCallback); + mTestLooper.dispatchAll(); + assertEquals(ADDR_PLAYBACK_3, mLogicalAddress); + } + + @Test + public void testAllocateLogicalAddress_PlaybackNonPreferred_2_0_AllOccupied() { + mCecVersion = Constants.VERSION_2_0; + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_BACKUP_1, SendMessageResult.SUCCESS); + mNativeWrapper.setPollAddressResponse(ADDR_BACKUP_2, SendMessageResult.SUCCESS); + mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); + mTestLooper.dispatchAll(); + assertEquals(ADDR_UNREGISTERED, mLogicalAddress); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index debf20bffe28..470294073535 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -66,10 +66,67 @@ public class HdmiCecMessageValidatorTest { @Test public void isValid_reportPowerStatus() { assertMessageValidity("04:90:00").isEqualTo(OK); + assertMessageValidity("04:90:03:05").isEqualTo(OK); assertMessageValidity("0F:90:00").isEqualTo(ERROR_DESTINATION); assertMessageValidity("F0:90").isEqualTo(ERROR_SOURCE); assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("04:90:04").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_menuRequest() { + assertMessageValidity("40:8D:00").isEqualTo(OK); + assertMessageValidity("40:8D:02:04").isEqualTo(OK); + + assertMessageValidity("0F:8D:00").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:8D").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:8D").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:8D:03").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_menuStatus() { + assertMessageValidity("40:8E:00").isEqualTo(OK); + assertMessageValidity("40:8E:01:00").isEqualTo(OK); + + assertMessageValidity("0F:8E:00").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:8E").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:8E").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:8E:02").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_setSystemAudioMode() { + assertMessageValidity("40:72:00").isEqualTo(OK); + assertMessageValidity("4F:72:01:03").isEqualTo(OK); + + assertMessageValidity("F0:72").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:72").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:72:02").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_systemAudioModeStatus() { + assertMessageValidity("40:7E:00").isEqualTo(OK); + assertMessageValidity("40:7E:01:01").isEqualTo(OK); + + assertMessageValidity("0F:7E:00").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:7E").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:7E").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:7E:02").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_setAudioRate() { + assertMessageValidity("40:9A:00").isEqualTo(OK); + assertMessageValidity("40:9A:03").isEqualTo(OK); + assertMessageValidity("40:9A:06:02").isEqualTo(OK); + + assertMessageValidity("0F:9A:00").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:9A").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:9A").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:9A:07").isEqualTo(ERROR_PARAMETER); } @Test @@ -110,6 +167,21 @@ public class HdmiCecMessageValidatorTest { assertMessageValidity("40:47:4C:69:7F").isEqualTo(ERROR_PARAMETER); } + @Test + public void isValid_recordStatus() { + assertMessageValidity("40:0A:01").isEqualTo(OK); + assertMessageValidity("40:0A:13").isEqualTo(OK); + assertMessageValidity("40:0A:1F:04:01").isEqualTo(OK); + + assertMessageValidity("0F:0A:01").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:0A:01").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:0A").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:0A:00").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:0A:0F").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:0A:1D").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:0A:30").isEqualTo(ERROR_PARAMETER); + } + private IntegerSubject assertMessageValidity(String message) { return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message))); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 382ae8218729..68b46c408028 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -382,9 +382,13 @@ public class HdmiControlServiceTest { assertThat(callback2.mVolumeControlEnabled).isTrue(); } - @Test public void getCecVersion_default() { + // Set the Settings value to "null" to emulate it being empty and force the default value. + Settings.Global.putString(mContextSpy.getContentResolver(), + Settings.Global.HDMI_CEC_VERSION, + null); + mHdmiControlService.setControlEnabled(true); assertThat(mHdmiControlService.getCecVersion()).isEqualTo(Constants.VERSION_1_4); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java index d8f5c4c8f58c..a8f3acf8726f 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.hardware.hdmi.HdmiDeviceInfo; import android.platform.test.annotations.Presubmit; import android.util.Slog; @@ -235,4 +236,366 @@ public class HdmiUtilsTest { assertThat(HdmiUtils.pathRelationship(0x1234, 0x1234)) .isEqualTo(Constants.PATH_RELATIONSHIP_SAME); } + + @Test + public void getTypeFromAddress() { + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TV)).containsExactly( + HdmiDeviceInfo.DEVICE_TV); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_RECORDER_1)).containsExactly( + HdmiDeviceInfo.DEVICE_RECORDER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_RECORDER_2)).containsExactly( + HdmiDeviceInfo.DEVICE_RECORDER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_1)).containsExactly( + HdmiDeviceInfo.DEVICE_TUNER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_PLAYBACK_1)).containsExactly( + HdmiDeviceInfo.DEVICE_PLAYBACK); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_AUDIO_SYSTEM)).containsExactly( + HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_2)).containsExactly( + HdmiDeviceInfo.DEVICE_TUNER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_3)).containsExactly( + HdmiDeviceInfo.DEVICE_TUNER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_PLAYBACK_2)).containsExactly( + HdmiDeviceInfo.DEVICE_PLAYBACK); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_RECORDER_3)).containsExactly( + HdmiDeviceInfo.DEVICE_RECORDER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_4)).containsExactly( + HdmiDeviceInfo.DEVICE_TUNER); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_PLAYBACK_3)).containsExactly( + HdmiDeviceInfo.DEVICE_PLAYBACK); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_BACKUP_1)).containsExactly( + HdmiDeviceInfo.DEVICE_PLAYBACK, HdmiDeviceInfo.DEVICE_RECORDER, + HdmiDeviceInfo.DEVICE_TUNER, HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_BACKUP_2)).containsExactly( + HdmiDeviceInfo.DEVICE_PLAYBACK, HdmiDeviceInfo.DEVICE_RECORDER, + HdmiDeviceInfo.DEVICE_TUNER, HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR); + assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_SPECIFIC_USE)).containsExactly( + HdmiDeviceInfo.DEVICE_TV); + } + + @Test + public void isEligibleAddressForDevice() { + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_TV)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_TV)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_TV)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_TV)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_TV)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_TV)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_TV)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_TV)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_RECORDER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_RECORDER_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_RECORDER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_RECORDER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_RECORDER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_RECORDER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_RECORDER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_RECORDER_1)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_RECORDER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_RECORDER_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_RECORDER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_RECORDER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_RECORDER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_RECORDER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_RECORDER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_RECORDER_2)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_TUNER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_TUNER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_TUNER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_TUNER_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_TUNER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_TUNER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_TUNER_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_TUNER_1)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_PLAYBACK_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_PLAYBACK_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_PLAYBACK_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_PLAYBACK_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_PLAYBACK_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_PLAYBACK_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_PLAYBACK_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_PLAYBACK_1)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_AUDIO_SYSTEM)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_AUDIO_SYSTEM)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_TUNER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_TUNER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_TUNER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_TUNER_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_TUNER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_TUNER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_TUNER_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_TUNER_2)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_TUNER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_TUNER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_TUNER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_TUNER_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_TUNER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_TUNER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_TUNER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_TUNER_3)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_PLAYBACK_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_PLAYBACK_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_PLAYBACK_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_PLAYBACK_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_PLAYBACK_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_PLAYBACK_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_PLAYBACK_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_PLAYBACK_2)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_RECORDER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_RECORDER_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_RECORDER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_RECORDER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_RECORDER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_RECORDER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_RECORDER_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_RECORDER_3)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_TUNER_4)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_TUNER_4)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_TUNER_4)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_TUNER_4)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_TUNER_4)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_TUNER_4)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_TUNER_4)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_TUNER_4)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_PLAYBACK_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_PLAYBACK_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_PLAYBACK_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_PLAYBACK_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_PLAYBACK_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_PLAYBACK_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_PLAYBACK_3)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_PLAYBACK_3)).isFalse(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_BACKUP_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_BACKUP_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_BACKUP_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_BACKUP_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_BACKUP_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_BACKUP_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_BACKUP_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_BACKUP_1)).isTrue(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_BACKUP_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_BACKUP_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_BACKUP_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_BACKUP_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_BACKUP_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_BACKUP_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_BACKUP_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_BACKUP_2)).isTrue(); + + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV, + Constants.ADDR_SPECIFIC_USE)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR, + Constants.ADDR_SPECIFIC_USE)).isFalse(); + } + + @Test + public void isEligibleAddressForCecVersion_1_4() { + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_TV)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_RECORDER_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_RECORDER_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_TUNER_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_PLAYBACK_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_AUDIO_SYSTEM)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_TUNER_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_TUNER_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_PLAYBACK_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_RECORDER_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_TUNER_4)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_PLAYBACK_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_BACKUP_1)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_BACKUP_2)).isFalse(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4, + Constants.ADDR_SPECIFIC_USE)).isTrue(); + } + + @Test + public void isEligibleAddressForCecVersion_2_0() { + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_TV)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_RECORDER_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_RECORDER_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_TUNER_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_PLAYBACK_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_AUDIO_SYSTEM)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_TUNER_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_TUNER_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_PLAYBACK_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_RECORDER_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_TUNER_4)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_PLAYBACK_3)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_BACKUP_1)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_BACKUP_2)).isTrue(); + assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0, + Constants.ADDR_SPECIFIC_USE)).isTrue(); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 6255630712ae..95c881e1d927 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -63,7 +63,6 @@ import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.LegacyPermissionDataProvider; -import com.android.server.pm.permission.PermissionSettings; import com.google.common.truth.Truth; @@ -92,8 +91,6 @@ public class PackageManagerSettingsTests { private static final int TEST_RESOURCE_ID = 2131231283; @Mock - PermissionSettings mPermissionSettings; - @Mock RuntimePermissionsPersistence mRuntimePermissionsPersistence; @Mock LegacyPermissionDataProvider mPermissionDataProvider; @@ -117,7 +114,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); verifyKeySetMetaData(settings); @@ -131,7 +128,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -147,7 +144,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue())); @@ -169,13 +166,13 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); settings.writeLPr(); // Create Settings again to make it read from the new files - settings = new Settings(context.getFilesDir(), mPermissionSettings, + settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -199,7 +196,7 @@ public class PackageManagerSettingsTests { writePackageRestrictions_noSuspendingPackageXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2)); @@ -223,7 +220,7 @@ public class PackageManagerSettingsTests { writePackageRestrictions_noSuspendParamsMapXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); settingsUnderTest.readPackageRestrictionsLPr(0); @@ -251,7 +248,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_suspendInfo() { final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); @@ -349,7 +346,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_distractionFlags() { final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); @@ -393,7 +390,7 @@ public class PackageManagerSettingsTests { public void testWriteReadUsesStaticLibraries() { final Context context = InstrumentationRegistry.getTargetContext(); final Object lock = new Object(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); ps1.appId = Process.FIRST_APPLICATION_UID; @@ -469,7 +466,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -642,7 +639,7 @@ public class PackageManagerSettingsTests { public void testUpdatePackageSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings, + final Settings testSettings01 = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); @@ -752,7 +749,7 @@ public class PackageManagerSettingsTests { public void testCreateNewSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings, + final Settings testSettings01 = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 0c6d6388b458..c43050386dcf 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -45,6 +45,11 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; +import static com.android.server.usage.AppStandbyController.DEFAULT_ELAPSED_TIME_THRESHOLDS; +import static com.android.server.usage.AppStandbyController.DEFAULT_SCREEN_TIME_THRESHOLDS; +import static com.android.server.usage.AppStandbyController.MINIMUM_ELAPSED_TIME_THRESHOLDS; +import static com.android.server.usage.AppStandbyController.MINIMUM_SCREEN_TIME_THRESHOLDS; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -73,6 +78,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; import android.util.ArraySet; import android.view.Display; @@ -184,6 +190,19 @@ public class AppStandbyControllerTests { int[] mRunningUsers = new int[] {USER_ID}; List<UserHandle> mCrossProfileTargets = Collections.emptyList(); boolean mDeviceIdleMode = false; + DeviceConfig.Properties.Builder mSettingsBuilder = + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_APP_STANDBY) + .setLong("screen_threshold_active", 0) + .setLong("screen_threshold_working_set", 0) + .setLong("screen_threshold_frequent", 0) + .setLong("screen_threshold_rare", HOUR_MS) + // screen_threshold_restricted intentionally skipped + .setLong("elapsed_threshold_active", 0) + .setLong("elapsed_threshold_working_set", WORKING_SET_THRESHOLD) + .setLong("elapsed_threshold_frequent", FREQUENT_THRESHOLD) + .setLong("elapsed_threshold_rare", RARE_THRESHOLD) + .setLong("elapsed_threshold_restricted", RESTRICTED_THRESHOLD); + DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener; MyInjector(Context context, Looper looper) { super(context, looper); @@ -285,10 +304,9 @@ public class AppStandbyControllerTests { } @Override - String getAppIdleSettings() { - return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/" - + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD - + "/" + RESTRICTED_THRESHOLD; + @NonNull + DeviceConfig.Properties getDeviceConfigProperties(String... keys) { + return mSettingsBuilder.build(); } @Override @@ -301,6 +319,12 @@ public class AppStandbyControllerTests { return mCrossProfileTargets; } + @Override + public void registerDeviceConfigPropertiesChangedListener( + @NonNull DeviceConfig.OnPropertiesChangedListener listener) { + mPropertiesChangedListener = listener; + } + // Internal methods void setDisplayOn(boolean on) { @@ -867,6 +891,25 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RARE); } + /** Test that timeouts still work properly even if invalid configuration values are set. */ + @Test + public void testTimeout_InvalidThresholds() throws Exception { + mInjector.mSettingsBuilder + .setLong("screen_threshold_active", -1) + .setLong("screen_threshold_working_set", -1) + .setLong("screen_threshold_frequent", -1) + .setLong("screen_threshold_rare", -1) + .setLong("screen_threshold_restricted", -1) + .setLong("elapsed_threshold_active", -1) + .setLong("elapsed_threshold_working_set", -1) + .setLong("elapsed_threshold_frequent", -1) + .setLong("elapsed_threshold_rare", -1) + .setLong("elapsed_threshold_restricted", -1); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + testTimeout(); + } + /** * Test that setAppStandbyBucket to RESTRICTED doesn't change the bucket until the usage * timeout has passed. @@ -1554,6 +1597,132 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1); } + @Test + public void testChangingSettings_ElapsedThreshold_Invalid() { + mInjector.mSettingsBuilder + .setLong("elapsed_threshold_active", -1) + .setLong("elapsed_threshold_working_set", -1) + .setLong("elapsed_threshold_frequent", -1) + .setLong("elapsed_threshold_rare", -1) + .setLong("elapsed_threshold_restricted", -1); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + for (int i = 0; i < MINIMUM_ELAPSED_TIME_THRESHOLDS.length; ++i) { + assertEquals(MINIMUM_ELAPSED_TIME_THRESHOLDS[i], + mController.mAppStandbyElapsedThresholds[i]); + } + } + + @Test + public void testChangingSettings_ElapsedThreshold_Valid() { + // Effectively clear values + mInjector.mSettingsBuilder + .setString("elapsed_threshold_active", null) + .setString("elapsed_threshold_working_set", null) + .setString("elapsed_threshold_frequent", null) + .setString("elapsed_threshold_rare", null) + .setString("elapsed_threshold_restricted", null); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + for (int i = 0; i < DEFAULT_ELAPSED_TIME_THRESHOLDS.length; ++i) { + assertEquals(DEFAULT_ELAPSED_TIME_THRESHOLDS[i], + mController.mAppStandbyElapsedThresholds[i]); + } + + // Set really high thresholds + mInjector.mSettingsBuilder + .setLong("elapsed_threshold_active", 90 * DAY_MS) + .setLong("elapsed_threshold_working_set", 91 * DAY_MS) + .setLong("elapsed_threshold_frequent", 92 * DAY_MS) + .setLong("elapsed_threshold_rare", 93 * DAY_MS) + .setLong("elapsed_threshold_restricted", 94 * DAY_MS); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + for (int i = 0; i < mController.mAppStandbyElapsedThresholds.length; ++i) { + assertEquals((90 + i) * DAY_MS, mController.mAppStandbyElapsedThresholds[i]); + } + + // Only set a few values + mInjector.mSettingsBuilder + .setString("elapsed_threshold_active", null) + .setLong("elapsed_threshold_working_set", 31 * DAY_MS) + .setLong("elapsed_threshold_frequent", 62 * DAY_MS) + .setString("elapsed_threshold_rare", null) + .setLong("elapsed_threshold_restricted", 93 * DAY_MS); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + + assertEquals(DEFAULT_ELAPSED_TIME_THRESHOLDS[0], + mController.mAppStandbyElapsedThresholds[0]); + assertEquals(31 * DAY_MS, mController.mAppStandbyElapsedThresholds[1]); + assertEquals(62 * DAY_MS, mController.mAppStandbyElapsedThresholds[2]); + assertEquals(DEFAULT_ELAPSED_TIME_THRESHOLDS[3], + mController.mAppStandbyElapsedThresholds[3]); + assertEquals(93 * DAY_MS, mController.mAppStandbyElapsedThresholds[4]); + } + + @Test + public void testChangingSettings_ScreenThreshold_Invalid() { + mInjector.mSettingsBuilder + .setLong("screen_threshold_active", -1) + .setLong("screen_threshold_working_set", -1) + .setLong("screen_threshold_frequent", -1) + .setLong("screen_threshold_rare", -1) + .setLong("screen_threshold_restricted", -1); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + for (int i = 0; i < MINIMUM_SCREEN_TIME_THRESHOLDS.length; ++i) { + assertEquals(MINIMUM_SCREEN_TIME_THRESHOLDS[i], + mController.mAppStandbyScreenThresholds[i]); + } + } + + @Test + public void testChangingSettings_ScreenThreshold_Valid() { + // Effectively clear values + mInjector.mSettingsBuilder + .setString("screen_threshold_active", null) + .setString("screen_threshold_working_set", null) + .setString("screen_threshold_frequent", null) + .setString("screen_threshold_rare", null) + .setString("screen_threshold_restricted", null); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + for (int i = 0; i < DEFAULT_SCREEN_TIME_THRESHOLDS.length; ++i) { + assertEquals(DEFAULT_SCREEN_TIME_THRESHOLDS[i], + mController.mAppStandbyScreenThresholds[i]); + } + + // Set really high thresholds + mInjector.mSettingsBuilder + .setLong("screen_threshold_active", 90 * DAY_MS) + .setLong("screen_threshold_working_set", 91 * DAY_MS) + .setLong("screen_threshold_frequent", 92 * DAY_MS) + .setLong("screen_threshold_rare", 93 * DAY_MS) + .setLong("screen_threshold_restricted", 94 * DAY_MS); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + for (int i = 0; i < mController.mAppStandbyScreenThresholds.length; ++i) { + assertEquals((90 + i) * DAY_MS, mController.mAppStandbyScreenThresholds[i]); + } + + // Only set a few values + mInjector.mSettingsBuilder + .setString("screen_threshold_active", null) + .setLong("screen_threshold_working_set", 31 * DAY_MS) + .setLong("screen_threshold_frequent", 62 * DAY_MS) + .setString("screen_threshold_rare", null) + .setLong("screen_threshold_restricted", 93 * DAY_MS); + mInjector.mPropertiesChangedListener + .onPropertiesChanged(mInjector.getDeviceConfigProperties()); + + assertEquals(DEFAULT_SCREEN_TIME_THRESHOLDS[0], mController.mAppStandbyScreenThresholds[0]); + assertEquals(31 * DAY_MS, mController.mAppStandbyScreenThresholds[1]); + assertEquals(62 * DAY_MS, mController.mAppStandbyScreenThresholds[2]); + assertEquals(DEFAULT_SCREEN_TIME_THRESHOLDS[3], mController.mAppStandbyScreenThresholds[3]); + assertEquals(93 * DAY_MS, mController.mAppStandbyScreenThresholds[4]); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index e304083dfd27..c50792866582 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -126,6 +126,7 @@ public class ManagedServicesTest extends UiServiceTestCase { users.add(mTen); users.add(new UserInfo(11, "11", 0)); users.add(new UserInfo(12, "12", 0)); + users.add(new UserInfo(13, "13", 0)); for (UserInfo user : users) { when(mUm.getUserInfo(eq(user.id))).thenReturn(user); } @@ -135,6 +136,7 @@ public class ManagedServicesTest extends UiServiceTestCase { profileIds.add(11); profileIds.add(10); profileIds.add(12); + profileIds.add(13); when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); mVersionString = "2"; @@ -145,11 +147,13 @@ public class ManagedServicesTest extends UiServiceTestCase { mExpectedPrimaryPackages.put(10, "this.is.another.package"); mExpectedPrimaryPackages.put(11, ""); mExpectedPrimaryPackages.put(12, "bananas!"); + mExpectedPrimaryPackages.put(13, "non.user.set.package"); mExpectedPrimaryComponentNames = new ArrayMap<>(); mExpectedPrimaryComponentNames.put(0, "this.is.a.package.name/Ba:another.package/B1"); mExpectedPrimaryComponentNames.put(10, "this.is.another.package/M1"); mExpectedPrimaryComponentNames.put(11, ""); mExpectedPrimaryComponentNames.put(12, "bananas!/Bananas!"); + mExpectedPrimaryComponentNames.put(13, "non.user.set.package/M1"); mExpectedPrimary.put(APPROVAL_BY_PACKAGE, mExpectedPrimaryPackages); mExpectedPrimary.put(APPROVAL_BY_COMPONENT, mExpectedPrimaryComponentNames); @@ -341,6 +345,35 @@ public class ManagedServicesTest extends UiServiceTestCase { } } + /** Test that restore correctly parses the user_set attribute. */ + @Test + public void testReadXml_restoresUserSet() throws Exception { + for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { + ManagedServices service = + new TestManagedServices( + getContext(), mLock, mUserProfiles, mIpm, approvalLevel); + String testPackage = "user.test.package"; + String testComponent = "user.test.component/C1"; + String resolvedValue = + (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; + String xmlEntry = getXmlEntry(resolvedValue, 0, true, false); + XmlPullParser parser = getParserWithEntries(service, xmlEntry); + + service.readXml(parser, null, true, 0); + + assertFalse("Failed while parsing xml:\n" + xmlEntry, + service.isPackageOrComponentUserSet(resolvedValue, 0)); + + xmlEntry = getXmlEntry(resolvedValue, 0, true, true); + parser = getParserWithEntries(service, xmlEntry); + + service.readXml(parser, null, true, 0); + + assertTrue("Failed while parsing xml:\n" + xmlEntry, + service.isPackageOrComponentUserSet(resolvedValue, 0)); + } + } + /** Test that restore ignores the user id attribute and applies the data to the target user. */ @Test public void testWriteReadXml_writeReadDefaults() throws Exception { @@ -374,7 +407,6 @@ public class ManagedServicesTest extends UiServiceTestCase { assertEquals(1, defaults.size()); assertEquals(new ComponentName("package", "class"), defaults.valueAt(0)); - } @Test @@ -628,6 +660,47 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void testWriteXml_writesUserSet() throws Exception { + for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { + ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, + mIpm, approvalLevel); + loadXml(service); + + XmlSerializer serializer = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + service.writeXml(serializer, false, UserHandle.USER_ALL); + serializer.endDocument(); + serializer.flush(); + + XmlPullParser parser = Xml.newPullParser(); + byte[] rawOutput = baos.toByteArray(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(rawOutput)), null); + parser.nextTag(); + for (UserInfo userInfo : mUm.getUsers()) { + service.readXml(parser, null, true, userInfo.id); + } + + String resolvedUserSetComponent = approvalLevel == APPROVAL_BY_PACKAGE + ? mExpectedPrimaryPackages.get(10) + : mExpectedPrimaryComponentNames.get(10); + String resolvedNonUserSetComponent = approvalLevel == APPROVAL_BY_PACKAGE + ? mExpectedPrimaryPackages.get(13) + : mExpectedPrimaryComponentNames.get(13); + + try { + assertFalse(service.isPackageOrComponentUserSet(resolvedNonUserSetComponent, 13)); + assertTrue(service.isPackageOrComponentUserSet(resolvedUserSetComponent, 10)); + } catch (AssertionError e) { + throw new AssertionError( + "Assertion failed while parsing xml:\n" + new String(rawOutput), e); + } + } + } + + @Test public void rebindServices_onlyBindsExactMatchesIfComponent() throws Exception { // If the primary and secondary lists contain component names, only those components within // the package should be matched @@ -965,6 +1038,7 @@ public class ManagedServicesTest extends UiServiceTestCase { allowedPackages.add("package"); allowedPackages.add("component"); allowedPackages.add("bananas!"); + allowedPackages.add("non.user.set.package"); Set<String> actual = service.getAllowedPackages(); assertEquals(allowedPackages.size(), actual.size()); @@ -1037,6 +1111,9 @@ public class ManagedServicesTest extends UiServiceTestCase { expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!")); expected.put(12, expected12); expected.put(11, new ArraySet<>()); + ArraySet<ComponentName> expected13 = new ArraySet<>(); + expected13.add(ComponentName.unflattenFromString("non.user.set.package/M1")); + expected.put(13, expected13); SparseArray<ArraySet<ComponentName>> actual = service.getAllowedComponents(mUserProfiles.getCurrentProfileIds()); @@ -1309,6 +1386,15 @@ public class ManagedServicesTest extends UiServiceTestCase { } private void loadXml(ManagedServices service) throws Exception { + String xmlString = createXml(service); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xmlString.getBytes())), null); + parser.nextTag(); + service.readXml(parser, null, false, UserHandle.USER_ALL); + } + + private String createXml(ManagedServices service) { final StringBuffer xml = new StringBuffer(); String xmlTag = service.getConfig().xmlTag; xml.append("<" + xmlTag @@ -1316,8 +1402,9 @@ public class ManagedServicesTest extends UiServiceTestCase { + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "") + ">\n"); for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) { + String pkgOrCmp = mExpectedPrimary.get(service.mApprovalLevel).get(userId); xml.append(getXmlEntry( - mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true)); + pkgOrCmp, userId, true, !(pkgOrCmp.startsWith("non.user.set.package")))); } for (int userId : mExpectedSecondary.get(service.mApprovalLevel).keySet()) { xml.append(getXmlEntry( @@ -1333,11 +1420,7 @@ public class ManagedServicesTest extends UiServiceTestCase { + ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n"); xml.append("</" + xmlTag + ">"); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream( - new ByteArrayInputStream(xml.toString().getBytes())), null); - parser.nextTag(); - service.readXml(parser, null, false, UserHandle.USER_ALL); + return xml.toString(); } private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) @@ -1524,10 +1607,15 @@ public class ManagedServicesTest extends UiServiceTestCase { } private String getXmlEntry(String approved, int userId, boolean isPrimary) { + return getXmlEntry(approved, userId, isPrimary, true); + } + + private String getXmlEntry(String approved, int userId, boolean isPrimary, boolean userSet) { return "<" + ManagedServices.TAG_MANAGED_SERVICES + " " + ManagedServices.ATT_USER_ID + "=\"" + userId +"\" " + ManagedServices.ATT_IS_PRIMARY + "=\"" + isPrimary +"\" " + ManagedServices.ATT_APPROVED_LIST + "=\"" + approved +"\" " + + ManagedServices.ATT_USER_SET + "=\"" + (userSet ? approved : "") + "\" " + "/>\n"; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 5796e848ff6e..f649911b6bb9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -134,7 +134,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { verify(mNm, never()).setDefaultAssistantForUser(anyInt()); verify(mAssistants, times(1)).addApprovedList( - new ComponentName("b", "b").flattenToString(),10, true); + new ComponentName("b", "b").flattenToString(), 10, true, null); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index b77f8c5eb8cd..fdc089ee2f7e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -120,7 +120,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(canBubble(i), ranking.canBubble()); assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive()); assertEquals(isConversation(i), ranking.isConversation()); - assertEquals(getShortcutInfo(i).getId(), ranking.getShortcutInfo().getId()); + assertEquals(getShortcutInfo(i).getId(), ranking.getConversationShortcutInfo().getId()); assertEquals(getRankingAdjustment(i), ranking.getRankingAdjustment()); } } @@ -191,7 +191,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { tweak.canBubble(), tweak.visuallyInterruptive(), tweak.isConversation(), - tweak.getShortcutInfo(), + tweak.getConversationShortcutInfo(), tweak.getRankingAdjustment(), tweak.isBubble() ); @@ -440,7 +440,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); assertEquals(comment, a.canBubble(), b.canBubble()); assertEquals(comment, a.isConversation(), b.isConversation()); - assertEquals(comment, a.getShortcutInfo().getId(), b.getShortcutInfo().getId()); + assertEquals(comment, a.getConversationShortcutInfo().getId(), b.getConversationShortcutInfo().getId()); assertActionsEqual(a.getSmartActions(), b.getSmartActions()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 740505e0d3d7..87aaba2b164a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -111,9 +111,9 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; -import android.content.IIntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -2880,16 +2880,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true); + mBinderService.setNotificationListenerAccessGrantedForUser( + c, user.getIdentifier(), true, true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, true); + c.flattenToString(), user.getIdentifier(), true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), false, true); + c.flattenToString(), user.getIdentifier(), false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test @@ -2958,12 +2959,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetListenerAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGranted(c, true); + mBinderService.setNotificationListenerAccessGranted(c, true, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), 0, false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3112,12 +3113,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetListenerAccess_onLowRam() throws Exception { when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGranted(c, true); + mBinderService.setNotificationListenerAccessGranted(c, true, true); verify(mListeners).setPackageOrComponentEnabled( - anyString(), anyInt(), anyBoolean(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mConditionProviders).setPackageOrComponentEnabled( - anyString(), anyInt(), anyBoolean(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mAssistants).migrateToXml(); verify(mAssistants).resetDefaultAssistantsIfNecessary(); } @@ -3160,14 +3161,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGranted(c, true); + mBinderService.setNotificationListenerAccessGranted(c, true, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), 0, false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 3e779a9b2435..bf742b7f96dc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -218,6 +218,22 @@ public class PreferencesHelperTest extends UiServiceTestCase { return null; }).when(mTestIContentProvider).canonicalizeAsync(any(), any(), any(), any()); doAnswer(invocation -> { + String callingPkg = invocation.getArgument(0); + String featureId = invocation.getArgument(1); + Uri uri = invocation.getArgument(2); + RemoteCallback cb = invocation.getArgument(3); + IContentProvider mock = (IContentProvider) (invocation.getMock()); + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + try { + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + mock.uncanonicalize(callingPkg, featureId, uri)); + } catch (RemoteException e) { /* consume */ } + cb.sendResult(bundle); + }); + return null; + }).when(mTestIContentProvider).uncanonicalizeAsync(any(), any(), any(), any()); + doAnswer(invocation -> { Uri uri = invocation.getArgument(0); RemoteCallback cb = invocation.getArgument(1); IContentProvider mock = (IContentProvider) (invocation.getMock()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java index 54b2b3b4a009..3220d1d6a990 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,6 +32,7 @@ import android.os.Binder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; @@ -41,6 +43,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + /** * Build/Install/Run: * atest WmTests:DisplayAreaOrganizerTest @@ -89,27 +93,39 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { } @Test + public void testRegisterOrganizer() throws RemoteException { + IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + List<DisplayAreaAppearedInfo> infos = mWm.mAtmService.mWindowOrganizerController + .mDisplayAreaOrganizerController + .registerOrganizer(organizer, FEATURE_VENDOR_FIRST).getList(); + + // Return a list contains the DA, and no onDisplayAreaAppeared triggered. + assertThat(infos).hasSize(1); + assertThat(infos.get(0).getDisplayAreaInfo().token) + .isEqualTo(mTestDisplayArea.getDisplayAreaInfo().token); + verify(organizer, never()).onDisplayAreaAppeared(any(DisplayAreaInfo.class), + any(SurfaceControl.class)); + } + + @Test public void testAppearedVanished() throws RemoteException { IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); - verify(organizer) - .onDisplayAreaAppeared(any(DisplayAreaInfo.class), any(SurfaceControl.class)); - unregisterMockOrganizer(organizer); + verify(organizer).onDisplayAreaVanished(any()); } @Test public void testChanged() throws RemoteException { IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); - verify(organizer) - .onDisplayAreaAppeared(any(DisplayAreaInfo.class), any(SurfaceControl.class)); - mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000)); + verify(organizer).onDisplayAreaInfoChanged(any()); Configuration tmpConfiguration = new Configuration(); tmpConfiguration.setTo(mDisplayContent.getRequestedOverrideConfiguration()); mDisplayContent.onRequestedOverrideConfigurationChanged(tmpConfiguration); + // Ensure it was still only called once if the bounds didn't change verify(organizer).onDisplayAreaInfoChanged(any()); } 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 c9e56fde0fb9..f52f98389872 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -36,6 +36,7 @@ import static android.view.Surface.ROTATION_90; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; @@ -1606,6 +1607,29 @@ public class DisplayContentTests extends WindowTestsBase { verifySizes(dc, forcedWidth, forcedHeight, forcedDensity); } + @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD }) + @Test + public void testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved() { + final WindowState child1 = createWindow(mAppWindow, FIRST_SUB_WINDOW, "child1"); + final WindowState nextImeTargetApp = createWindow(null /* parent */, + TYPE_BASE_APPLICATION, "nextImeTargetApp"); + spyOn(child1); + doReturn(true).when(child1).inSplitScreenWindowingMode(); + mDisplayContent.mInputMethodTarget = child1; + + spyOn(nextImeTargetApp); + spyOn(mAppWindow); + doReturn(true).when(nextImeTargetApp).canBeImeTarget(); + doReturn(true).when(nextImeTargetApp).isActivityTypeHome(); + doReturn(false).when(mAppWindow).canBeImeTarget(); + + child1.removeImmediately(); + + verify(mDisplayContent).computeImeTarget(true); + assertNull(mDisplayContent.mInputMethodInputTarget); + verify(child1, never()).needsRelativeLayeringToIme(); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 3ff859b55bdd..51e1ebc63cf2 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -158,7 +158,8 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P * * Asu is calculated based on 3GPP RSSI. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 * - * @return RSSI in ASU 0..31, 99, or UNAVAILABLE + * @return RSSI in ASU 0..31, 99, or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. */ @Override public int getAsuLevel() { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 19e70c8d1443..c982f490e0fd 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10571,6 +10571,9 @@ public class TelephonyManager { * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * + * If you want continuous updates of service state info, register a {@link PhoneStateListener} + * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event. + * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 41381c59482b..3e2a6eec37c4 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -109,8 +109,14 @@ public final class DataCallResponse implements Parcelable { */ public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3; + /** + * Indicates that data retry interval is not specified. Platform can determine when to + * perform data setup appropriately. + */ + public static final int RETRY_INTERVAL_UNDEFINED = -1; + private final @DataFailureCause int mCause; - private final int mSuggestedRetryTime; + private final long mSuggestedRetryTime; private final int mId; private final @LinkStatus int mLinkStatus; private final @ProtocolType int mProtocolType; @@ -172,7 +178,7 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; } - private DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id, + private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @LinkStatus int linkStatus, @ProtocolType int protocolType, @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, @@ -202,7 +208,7 @@ public final class DataCallResponse implements Parcelable { @VisibleForTesting public DataCallResponse(Parcel source) { mCause = source.readInt(); - mSuggestedRetryTime = source.readInt(); + mSuggestedRetryTime = source.readLong(); mId = source.readInt(); mLinkStatus = source.readInt(); mProtocolType = source.readInt(); @@ -229,8 +235,22 @@ public final class DataCallResponse implements Parcelable { /** * @return The suggested data retry time in milliseconds. + * + * @deprecated Use {@link #getRetryIntervalMillis()} instead. + */ + @Deprecated + public int getSuggestedRetryTime() { + return (int) mSuggestedRetryTime; + } + + /** + * @return The network suggested data retry interval in milliseconds. {@code Long.MAX_VALUE} + * indicates data retry should not occur. {@link #RETRY_INTERVAL_UNDEFINED} indicates network + * did not suggest any retry interval. */ - public int getSuggestedRetryTime() { return mSuggestedRetryTime; } + public long getRetryIntervalMillis() { + return mSuggestedRetryTime; + } /** * @return The unique id of the data connection. @@ -382,7 +402,7 @@ public final class DataCallResponse implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mCause); - dest.writeInt(mSuggestedRetryTime); + dest.writeLong(mSuggestedRetryTime); dest.writeInt(mId); dest.writeInt(mLinkStatus); dest.writeInt(mProtocolType); @@ -446,7 +466,7 @@ public final class DataCallResponse implements Parcelable { public static final class Builder { private @DataFailureCause int mCause; - private int mSuggestedRetryTime; + private long mSuggestedRetryTime = RETRY_INTERVAL_UNDEFINED; private int mId; @@ -494,9 +514,23 @@ public final class DataCallResponse implements Parcelable { * * @param suggestedRetryTime The suggested data retry time in milliseconds. * @return The same instance of the builder. + * + * @deprecated Use {@link #setRetryIntervalMillis(long)} instead. */ + @Deprecated public @NonNull Builder setSuggestedRetryTime(int suggestedRetryTime) { - mSuggestedRetryTime = suggestedRetryTime; + mSuggestedRetryTime = (long) suggestedRetryTime; + return this; + } + + /** + * Set the network suggested data retry interval. + * + * @param retryIntervalMillis The suggested data retry interval in milliseconds. + * @return The same instance of the builder. + */ + public @NonNull Builder setRetryIntervalMillis(long retryIntervalMillis) { + mSuggestedRetryTime = retryIntervalMillis; return this; } diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java index a5c254f1aca2..5b9f67efd95d 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -169,6 +169,12 @@ public class MockContentProvider extends ContentProvider { } @Override + public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri, + RemoteCallback callback) { + MockContentProvider.this.uncanonicalizeAsync(uri, callback); + } + + @Override public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args, ICancellationSignal cancellationSignal) throws RemoteException { return MockContentProvider.this.refresh(url, args); @@ -311,6 +317,18 @@ public class MockContentProvider extends ContentProvider { /** * @hide */ + @SuppressWarnings("deprecation") + public void uncanonicalizeAsync(Uri uri, RemoteCallback callback) { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, uncanonicalize(uri)); + callback.sendResult(bundle); + }); + } + + /** + * @hide + */ public boolean refresh(Uri url, Bundle args) { throw new UnsupportedOperationException("unimplemented mock method call"); } diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java index 223bcc59039d..82a1cf7d1796 100644 --- a/test-mock/src/android/test/mock/MockIContentProvider.java +++ b/test-mock/src/android/test/mock/MockIContentProvider.java @@ -162,12 +162,23 @@ public class MockIContentProvider implements IContentProvider { } @Override - public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) - throws RemoteException { + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override + @SuppressWarnings("deprecation") + public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri, + RemoteCallback remoteCallback) { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + uncanonicalize(callingPkg, featureId, uri)); + remoteCallback.sendResult(bundle); + }); + } + + @Override public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args, ICancellationSignal cancellationSignal) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java index 02f5286506a8..603c87519532 100644 --- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java +++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java @@ -19,6 +19,8 @@ package android.net.ipmemorystore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk; +import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirkParcelable; import android.os.Parcel; import android.os.Parcelable; @@ -46,7 +48,7 @@ public class ParcelableTests { builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); // lease will expire in two hours builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000); - // groupHint stays null this time around + // cluster stays null this time around builder.setDnsAddresses(Collections.emptyList()); builder.setMtu(18); in = builder.build(); @@ -69,7 +71,7 @@ public class ParcelableTests { // Verify that this test does not miss any new field added later. // If any field is added to NetworkAttributes it must be tested here for parceling // roundtrip. - assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields()) + assertEquals(6, Arrays.stream(NetworkAttributes.class.getDeclaredFields()) .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); } @@ -104,6 +106,22 @@ public class ParcelableTests { assertEquals(in.confidence, out.confidence, 0.01f /* delta */); } + @Test + public void testIPv6ProvisioningLossQuirkParceling() throws Exception { + final NetworkAttributes.Builder builder = new NetworkAttributes.Builder(); + final IPv6ProvisioningLossQuirkParcelable parcelable = + new IPv6ProvisioningLossQuirkParcelable(); + final long expiry = System.currentTimeMillis() + 7_200_000; + + parcelable.detectionCount = 3; + parcelable.quirkExpiry = expiry; // quirk info will expire in two hours + builder.setIpv6ProvLossQuirk(IPv6ProvisioningLossQuirk.fromStableParcelable(parcelable)); + final NetworkAttributes in = builder.build(); + + final NetworkAttributes out = new NetworkAttributes(parcelingRoundTrip(in.toParcelable())); + assertEquals(out.ipv6ProvisioningLossQuirk, in.ipv6ProvisioningLossQuirk); + } + private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception { final Parcel p = Parcel.obtain(); in.writeToParcel(p, /* flags */ 0); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 4081346f62f9..2a0c99c3bc52 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -6023,23 +6023,23 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mNetworkManagementService); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId)); - reset(mNetworkManagementService); + verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); + reset(mMockNetd); mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mNetworkManagementService); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - verify(mNetworkManagementService).clearDefaultNetId(); + verify(mMockNetd).networkClearDefault(); mCm.unregisterNetworkCallback(trustedCallback); } @@ -6143,6 +6143,7 @@ public class ConnectivityServiceTest { verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt()); assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute); verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME); verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), TYPE_MOBILE); @@ -6199,7 +6200,7 @@ public class ConnectivityServiceTest { .getStackedLinks(); assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0)); assertRoutesAdded(cellNetId, stackedDefault); - + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); // Change trivial linkproperties and see if stacked link is preserved. cellLp.addDnsServer(InetAddress.getByName("8.8.8.8")); mCellNetworkAgent.sendLinkProperties(cellLp); @@ -6230,6 +6231,7 @@ public class ConnectivityServiceTest { (lp) -> lp.getStackedLinks().size() == 0); verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); assertRoutesRemoved(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString()); networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, @@ -6238,6 +6240,7 @@ public class ConnectivityServiceTest { networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, (lp) -> lp.getStackedLinks().size() == 1); assertRoutesAdded(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); reset(mMockNetd); // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked @@ -6262,7 +6265,7 @@ public class ConnectivityServiceTest { // The interface removed callback happens but has no effect after stop is called. clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); networkCallback.assertNoCallback(); - + verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); verifyNoMoreInteractions(mMockNetd); verifyNoMoreInteractions(mMockDnsResolver); reset(mNetworkManagementService); @@ -6295,6 +6298,7 @@ public class ConnectivityServiceTest { networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null); assertRoutesAdded(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); // NAT64 prefix is removed. Expect that clat is stopped. mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */, @@ -6307,8 +6311,8 @@ public class ConnectivityServiceTest { verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, (lp) -> lp.getStackedLinks().size() == 0); + verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); verifyNoMoreInteractions(mMockNetd); - // Clean up. mCellNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java index fb84611cb662..ebbc0ef62548 100644 --- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java +++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java @@ -19,6 +19,7 @@ package com.android.server.net.ipmemorystore; import static org.junit.Assert.assertEquals; import android.net.ipmemorystore.NetworkAttributes; +import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -52,6 +53,8 @@ public class NetworkAttributesTest { } assertEquals(sum, NetworkAttributes.TOTAL_WEIGHT, EPSILON); + final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk = + new IPv6ProvisioningLossQuirk(3, System.currentTimeMillis() + 7_200_000); // Use directly the constructor with all attributes, and make sure that when compared // to itself the score is a clean 1.0f. final NetworkAttributes na = @@ -61,7 +64,7 @@ public class NetworkAttributesTest { "some hint", Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}), Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})), - 98); + 98, ipv6ProvisioningLossQuirk); assertEquals(1.0f, na.getNetworkGroupSamenessConfidence(na), EPSILON); } } diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp index 61ce44c3b5b9..2759e29d1620 100644 --- a/tools/validatekeymaps/Android.bp +++ b/tools/validatekeymaps/Android.bp @@ -16,6 +16,7 @@ cc_binary_host { static_libs: [ "libbase", + "libbinder", "libinput", "libutils", "libcutils", diff --git a/wifi/Android.bp b/wifi/Android.bp index 3040041038cb..52e3840c126e 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -75,25 +75,34 @@ test_access_hidden_api_whitelist = [ "//external/sl4a:__subpackages__", ] -// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility -// classes before they are renamed. -java_library { - name: "framework-wifi-pre-jarjar", +// defaults shared between `framework-wifi` & `framework-wifi-pre-jarjar` +// java_sdk_library `framework-wifi` needs sources to generate stubs, so it cannot reuse +// `framework-wifi-pre-jarjar` +java_defaults { + name: "framework-wifi-defaults", defaults: ["wifi-module-sdk-version-defaults"], - sdk_version: "module_current", static_libs: [ "framework-wifi-util-lib", "android.hardware.wifi-V1.0-java-constants", + "modules-utils-build", ], libs: [ - "framework-annotations-lib", "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage ], srcs: [ ":framework-wifi-updatable-sources", ":framework-wifi-util-lib-aidls", ], - // java_api_finder must accompany `srcs` +} + +// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility +// classes before they are renamed. +java_library { + name: "framework-wifi-pre-jarjar", + defaults: ["framework-wifi-defaults"], + sdk_version: "module_current", + libs: ["framework-annotations-lib"], + // java_api_finder must accompany `srcs` (`srcs` defined in `framework-wifi-defaults`) plugins: ["java_api_finder"], installable: false, visibility: [ @@ -107,18 +116,7 @@ java_sdk_library { name: "framework-wifi", defaults: [ "framework-module-defaults", - "wifi-module-sdk-version-defaults", - ], - static_libs: [ - "framework-wifi-util-lib", - "android.hardware.wifi-V1.0-java-constants", - ], - libs: [ - "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage - ], - srcs: [ - ":framework-wifi-updatable-sources", - ":framework-wifi-util-lib-aidls", + "framework-wifi-defaults", ], jarjar_rules: ":wifi-jarjar-rules", diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index 5dc5dfc02bce..7c592f0aec9f 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -336,8 +336,11 @@ package android.net.wifi { field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0 field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11 + field @Deprecated public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; // 0x3ec field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9 field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0 + field @Deprecated public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; // 0x3eb + field @Deprecated public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; // 0x3ea field @Deprecated public boolean allowAutojoin; field @Deprecated public int carrierId; field @Deprecated public String creatorName; diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index eef08b54f570..b0ff4bb27e70 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -36,6 +36,7 @@ rule android.net.ipmemorystore.IOnStatusListener* com.android.wifi.x.@0 rule android.net.ipmemorystore.NetworkAttributesParcelable* com.android.wifi.x.@0 rule android.net.ipmemorystore.SameL3NetworkResponseParcelable* com.android.wifi.x.@0 rule android.net.ipmemorystore.StatusParcelable* com.android.wifi.x.@0 +rule android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk* com.android.wifi.x.@0 # Net utils (includes Network Stack helper classes). rule android.net.DhcpResults* com.android.wifi.x.@0 @@ -123,3 +124,4 @@ rule com.android.internal.util.Preconditions* com.android.wifi.x.@0 rule com.android.internal.util.Protocol* com.android.wifi.x.@0 rule com.android.net.module.util.** com.android.wifi.x.@0 +rule com.android.modules.utils.** com.android.wifi.x.@0 diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java index cf54f26fc5e4..6bd4211d8214 100644 --- a/wifi/java/android/net/wifi/SoftApCapability.java +++ b/wifi/java/android/net/wifi/SoftApCapability.java @@ -21,10 +21,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.wifi.SoftApConfiguration.BandType; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; import android.os.Parcelable; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; @@ -176,7 +177,7 @@ public final class SoftApCapability implements Parcelable { */ @NonNull public int[] getSupportedChannelList(@BandType int band) { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } switch (band) { diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 5cbbb0dcdedf..2c53daaf55a7 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.MacAddress; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -31,6 +30,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.modules.utils.build.SdkLevel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -499,7 +499,7 @@ public final class SoftApConfiguration implements Parcelable { */ @SystemApi public @NonNull int[] getBands() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } int[] bands = new int[mChannels.size()]; @@ -535,7 +535,7 @@ public final class SoftApConfiguration implements Parcelable { */ @SystemApi public @NonNull SparseIntArray getChannels() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return mChannels; @@ -635,7 +635,7 @@ public final class SoftApConfiguration implements Parcelable { @SystemApi @MacRandomizationSetting public int getMacRandomizationSetting() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return mMacRandomizationSetting; @@ -1219,7 +1219,7 @@ public final class SoftApConfiguration implements Parcelable { @NonNull public Builder setMacRandomizationSetting( @MacRandomizationSetting int macRandomizationSetting) { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } mMacRandomizationSetting = macRandomizationSetting; diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java index cf61f81e6d22..9a16facfec26 100644 --- a/wifi/java/android/net/wifi/SoftApInfo.java +++ b/wifi/java/android/net/wifi/SoftApInfo.java @@ -20,11 +20,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.MacAddress; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.util.Preconditions; +import com.android.modules.utils.build.SdkLevel; import java.util.Objects; @@ -141,7 +141,7 @@ public final class SoftApInfo implements Parcelable { */ @Nullable public MacAddress getBssid() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return mBssid; @@ -180,7 +180,7 @@ public final class SoftApInfo implements Parcelable { * @return valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} */ public @WifiAnnotations.WifiStandard int getWifiStandard() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return mWifiStandard; diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 9a8a5ad4b41f..9ca64d2ae2ef 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -2074,27 +2074,42 @@ public class WifiConfiguration implements Parcelable { */ @RecentFailureReason private int mAssociationStatus = RECENT_FAILURE_NONE; + private long mLastUpdateTimeSinceBootMillis; /** * @param status the association status code for the recent failure */ - public void setAssociationStatus(@RecentFailureReason int status) { + public void setAssociationStatus(@RecentFailureReason int status, + long updateTimeSinceBootMs) { mAssociationStatus = status; + mLastUpdateTimeSinceBootMillis = updateTimeSinceBootMs; } /** * Sets the RecentFailure to NONE */ public void clear() { mAssociationStatus = RECENT_FAILURE_NONE; + mLastUpdateTimeSinceBootMillis = 0; } /** - * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE} or - * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}. + * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE}, + * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}, + * {@link #RECENT_FAILURE_MBO_OCE_DISCONNECT}, + * {@link #RECENT_FAILURE_REFUSED_TEMPORARILY}, + * {@link #RECENT_FAILURE_POOR_CHANNEL_CONDITIONS}. + * {@link #RECENT_FAILURE_DISCONNECTION_AP_BUSY} */ @RecentFailureReason public int getAssociationStatus() { return mAssociationStatus; } + + /** + * Get the timestamp the failure status is last updated, in milliseconds since boot. + */ + public long getLastUpdateTimeSinceBootMillis() { + return mLastUpdateTimeSinceBootMillis; + } } /** @@ -2111,7 +2126,11 @@ public class WifiConfiguration implements Parcelable { @IntDef(prefix = "RECENT_FAILURE_", value = { RECENT_FAILURE_NONE, RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA, - RECENT_FAILURE_MBO_OCE_DISCONNECT}) + RECENT_FAILURE_MBO_OCE_DISCONNECT, + RECENT_FAILURE_REFUSED_TEMPORARILY, + RECENT_FAILURE_POOR_CHANNEL_CONDITIONS, + RECENT_FAILURE_DISCONNECTION_AP_BUSY + }) public @interface RecentFailureReason {} /** @@ -2136,12 +2155,39 @@ public class WifiConfiguration implements Parcelable { public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; /** + * Failed to connect because the association is rejected by the AP. + * IEEE 802.11 association status code 30. + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; + + /** + * Failed to connect because of excess frame loss and/or poor channel conditions. + * IEEE 802.11 association status code 34. + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; + + /** + * Disconnected by the AP because the AP can't handle all the associated stations. + * IEEE 802.11 disconnection reason code 5. + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; + + /** * Get the failure reason for the most recent connection attempt, or * {@link #RECENT_FAILURE_NONE} if there was no failure. * * Failure reasons include: * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA} - * + * {@link #RECENT_FAILURE_MBO_OCE_DISCONNECT} + * {@link #RECENT_FAILURE_REFUSED_TEMPORARILY} + * {@link #RECENT_FAILURE_POOR_CHANNEL_CONDITIONS} + * {@link #RECENT_FAILURE_DISCONNECTION_AP_BUSY} * @hide */ @RecentFailureReason @@ -2479,7 +2525,8 @@ public class WifiConfiguration implements Parcelable { } } sbuf.append("recentFailure: ").append("Association Rejection code: ") - .append(recentFailure.getAssociationStatus()).append("\n"); + .append(recentFailure.getAssociationStatus()).append(", last update time: ") + .append(recentFailure.getLastUpdateTimeSinceBootMillis()).append("\n"); return sbuf.toString(); } @@ -2918,7 +2965,8 @@ public class WifiConfiguration implements Parcelable { numNoInternetAccessReports = source.numNoInternetAccessReports; noInternetAccessExpected = source.noInternetAccessExpected; shared = source.shared; - recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus()); + recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus(), + source.recentFailure.getLastUpdateTimeSinceBootMillis()); mRandomizedMacAddress = source.mRandomizedMacAddress; macRandomizationSetting = source.macRandomizationSetting; randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs; @@ -2995,6 +3043,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(shared ? 1 : 0); dest.writeString(mPasspointManagementObjectTree); dest.writeInt(recentFailure.getAssociationStatus()); + dest.writeLong(recentFailure.getLastUpdateTimeSinceBootMillis()); dest.writeParcelable(mRandomizedMacAddress, flags); dest.writeInt(macRandomizationSetting); dest.writeInt(osu ? 1 : 0); @@ -3071,7 +3120,7 @@ public class WifiConfiguration implements Parcelable { config.noInternetAccessExpected = in.readInt() != 0; config.shared = in.readInt() != 0; config.mPasspointManagementObjectTree = in.readString(); - config.recentFailure.setAssociationStatus(in.readInt()); + config.recentFailure.setAssociationStatus(in.readInt(), in.readLong()); config.mRandomizedMacAddress = in.readParcelable(null); config.macRandomizationSetting = in.readInt(); config.osu = in.readInt() != 0; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 78bf88b7ca78..8ee08f1610d6 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -44,7 +44,6 @@ import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.ProvisioningCallback; -import android.net.wifi.util.SdkLevelUtil; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -62,6 +61,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.utils.build.SdkLevel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -2493,7 +2493,7 @@ public class WifiManager { * @return true if this device supports multiple STA concurrency, false otherwise. */ public boolean isMultiStaConcurrencySupported() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return isFeatureSupported(WIFI_FEATURE_ADDITIONAL_STA); diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 0b368708d283..acae218b74e0 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -26,7 +26,6 @@ import android.annotation.SystemApi; import android.net.MacAddress; import android.net.NetworkCapabilities; import android.net.wifi.hotspot2.PasspointConfiguration; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; import android.os.Parcelable; import android.telephony.SubscriptionInfo; @@ -34,6 +33,8 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; +import com.android.modules.utils.build.SdkLevel; + import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.util.List; @@ -451,7 +452,7 @@ public final class WifiNetworkSuggestion implements Parcelable { * @return Instance of {@link Builder} to enable chaining of the builder method. */ public @NonNull Builder setSubscriptionId(int subId) { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } mSubscriptionId = subId; @@ -466,7 +467,7 @@ public final class WifiNetworkSuggestion implements Parcelable { * @return Instance of {@link Builder} to enable chaining of the builder method. */ public @NonNull Builder setPriorityGroup(int priorityGroup) { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } mPriorityGroup = priorityGroup; @@ -707,7 +708,7 @@ public final class WifiNetworkSuggestion implements Parcelable { */ @SystemApi public @NonNull Builder setOemPaid(boolean isOemPaid) { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } mIsNetworkOemPaid = isOemPaid; @@ -1204,7 +1205,7 @@ public final class WifiNetworkSuggestion implements Parcelable { */ @SystemApi public boolean isOemPaid() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return wifiConfiguration.oemPaid; @@ -1242,7 +1243,7 @@ public final class WifiNetworkSuggestion implements Parcelable { * @see Builder#setPriorityGroup(int) */ public int getPriorityGroup() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return priorityGroup; @@ -1252,7 +1253,7 @@ public final class WifiNetworkSuggestion implements Parcelable { * @see Builder#setSubscriptionId(int) */ public int getSubscriptionId() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return wifiConfiguration.subscriptionId; diff --git a/wifi/java/android/net/wifi/aware/Characteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java index 116786516b3b..9bdda7f7d323 100644 --- a/wifi/java/android/net/wifi/aware/Characteristics.java +++ b/wifi/java/android/net/wifi/aware/Characteristics.java @@ -17,11 +17,12 @@ package android.net.wifi.aware; import android.annotation.IntDef; -import android.net.wifi.util.SdkLevelUtil; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -92,7 +93,7 @@ public final class Characteristics implements Parcelable { * @return True if supported, false otherwise. */ public boolean isInstantCommunicationModeSupported() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } return mCharacteristics.getBoolean(KEY_IS_INSTANT_COMMUNICATION_MODE_SUPPORTED); diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index bb146e37d48c..67c60322e47a 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -29,7 +29,6 @@ import android.net.ConnectivityManager; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.wifi.util.HexEncoding; -import android.net.wifi.util.SdkLevelUtil; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -39,6 +38,8 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -210,7 +211,7 @@ public class WifiAwareManager { * or not (false). */ public boolean isDeviceAttached() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { @@ -229,7 +230,7 @@ public class WifiAwareManager { */ @SystemApi public void enableInstantCommunicationMode(boolean enable) { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { @@ -246,7 +247,7 @@ public class WifiAwareManager { * @return true if it is enabled, false otherwise. */ public boolean isInstantCommunicationModeEnabled() { - if (!SdkLevelUtil.isAtLeastS()) { + if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { diff --git a/wifi/java/android/net/wifi/util/SdkLevelUtil.java b/wifi/java/android/net/wifi/util/SdkLevelUtil.java deleted file mode 100644 index d08d4fd742b7..000000000000 --- a/wifi/java/android/net/wifi/util/SdkLevelUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.util; - -import android.os.Build; - -/** - * Utility to check the SDK version of the device that the code is running on. - * - * This can be used to disable new Wifi APIs added in Mainline updates on older SDK versions. - * - * Note: if certain functionality is gated with SdkLevelUtil, its corresponding unit tests should - * also be gated by the same condition. Then, those unit tests will only be exercised on a base - * system image satisfying that condition. - * Alternatively, it can be tested via static mocking. - * - * @hide - */ -public class SdkLevelUtil { - - /** This class is not instantiable. */ - private SdkLevelUtil() {} - - /** Returns true if the Android platform SDK is at least "S", false otherwise. */ - public static boolean isAtLeastS() { - // TODO(b/167575586): after S SDK finalization, this method should just be - // `return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;` - - // at least S: return true - // this condition only evaluates to true after S SDK finalization when VERSION_CODES.S - // is set to something like "31", before SDK finalization the value is "10000" - // Note that Build.VERSION_CODES.S is inlined at compile time. If it's inlined to 10000, - // this condition never evaluates to true. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - return true; - } - - // Assume for now that S = R + 1 - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { - return true; - } - - // R: check CODENAME - // Before S SDK finalization, SDK_INT = R = 30 i.e. remains on the previous version - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - // CODENAME = "REL" on R release builds - // CODENAME = "S" on S development builds - return "S".equals(Build.VERSION.CODENAME); - } - - // older than R: return false - return false; - } -} diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 6a39959e8cfd..b710a1492d8c 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -31,10 +31,11 @@ android_test { static_libs: [ "androidx.test.rules", "core-test-rules", + "frameworks-base-testutils", "guava", "mockito-target-minus-junit4", + "modules-utils-build", "net-tests-utils", - "frameworks-base-testutils", "truth-prebuilt", ], @@ -47,4 +48,8 @@ android_test { "device-tests", "mts", ], + + // static libs used by both framework-wifi & FrameworksWifiApiTests. Need to rename test usage + // to a different package name to prevent conflict with the copy in production code. + jarjar_rules: "test-jarjar-rules.txt", } diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java index 3c935058787b..702212b324f6 100644 --- a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java @@ -16,14 +16,15 @@ package android.net.wifi; -import android.net.wifi.util.SdkLevelUtil; -import android.os.Parcel; - import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import android.os.Parcel; + import androidx.test.filters.SmallTest; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Test; /** @@ -93,7 +94,7 @@ public class SoftApCapabilityTest { @Test(expected = IllegalArgumentException.class) public void testGetSupportedChannelListWithInvalidBand() { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD; diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index 7ee0fe858c5a..4a94f1f47962 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -22,12 +22,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.net.MacAddress; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; import android.util.SparseIntArray; import androidx.test.filters.SmallTest; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Test; import java.util.ArrayList; @@ -84,7 +85,7 @@ public class SoftApConfigurationTest { assertThat(original.getChannel()).isEqualTo(0); assertThat(original.isHiddenSsid()).isEqualTo(false); assertThat(original.getMaxNumberOfClients()).isEqualTo(0); - if (SdkLevelUtil.isAtLeastS()) { + if (SdkLevel.isAtLeastS()) { assertThat(original.getMacRandomizationSetting()) .isEqualTo(SoftApConfiguration.RANDOMIZATION_PERSISTENT); } @@ -140,7 +141,7 @@ public class SoftApConfigurationTest { .setClientControlByUserEnabled(true) .setBlockedClientList(testBlockedClientList) .setAllowedClientList(testAllowedClientList); - if (SdkLevelUtil.isAtLeastS()) { + if (SdkLevel.isAtLeastS()) { originalBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); } SoftApConfiguration original = originalBuilder.build(); @@ -156,7 +157,7 @@ public class SoftApConfigurationTest { assertThat(original.isClientControlByUserEnabled()).isEqualTo(true); assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList); assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList); - if (SdkLevelUtil.isAtLeastS()) { + if (SdkLevel.isAtLeastS()) { assertThat(original.getMacRandomizationSetting()) .isEqualTo(SoftApConfiguration.RANDOMIZATION_NONE); } diff --git a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java index 2821c35f9347..28758432119c 100644 --- a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java @@ -16,14 +16,15 @@ package android.net.wifi; +import static org.junit.Assert.assertEquals; + import android.net.MacAddress; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; -import static org.junit.Assert.assertEquals; - import androidx.test.filters.SmallTest; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Test; /** @@ -84,7 +85,7 @@ public class SoftApInfoTest { SoftApInfo info = new SoftApInfo(); assertEquals(info.getFrequency(), 0); assertEquals(info.getBandwidth(), SoftApInfo.CHANNEL_WIDTH_INVALID); - if (SdkLevelUtil.isAtLeastS()) { + if (SdkLevel.isAtLeastS()) { assertEquals(info.getBssid(), null); assertEquals(info.getWifiStandard(), ScanResult.WIFI_STANDARD_UNKNOWN); } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index bda776db733c..7340b145e8b2 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -86,7 +86,6 @@ import android.net.wifi.WifiManager.SoftApCallback; import android.net.wifi.WifiManager.SuggestionConnectionStatusListener; import android.net.wifi.WifiManager.TrafficStateCallback; import android.net.wifi.WifiManager.WifiConnectedNetworkScorer; -import android.net.wifi.util.SdkLevelUtil; import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; @@ -98,6 +97,8 @@ import android.util.SparseArray; import androidx.test.filters.SmallTest; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -1729,7 +1730,7 @@ public class WifiManagerTest { */ @Test public void testIsMultiStaConcurrencyOpenSupported() throws Exception { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); when(mWifiService.getSupportedFeatures()) .thenReturn(new Long(WIFI_FEATURE_ADDITIONAL_STA)); diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index bb84120da6ed..56e79983817f 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -27,11 +27,12 @@ import static org.junit.Assume.assumeTrue; import android.net.MacAddress; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.PasspointTestUtils; -import android.net.wifi.util.SdkLevelUtil; import android.os.Parcel; import androidx.test.filters.SmallTest; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Test; import java.security.cert.X509Certificate; @@ -198,7 +199,7 @@ public class WifiNetworkSuggestionTest { */ @Test public void testWifiNetworkSuggestionBuilderForOemPaidEnhancedOpenNetworkWithBssid() { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) @@ -953,7 +954,7 @@ public class WifiNetworkSuggestionTest { */ @Test public void testSimCredentialNetworkWithSubId() { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE); @@ -1254,7 +1255,7 @@ public class WifiNetworkSuggestionTest { */ @Test public void testSetIsNetworkAsOemPaid() { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) @@ -1272,7 +1273,7 @@ public class WifiNetworkSuggestionTest { */ @Test public void testSetIsNetworkAsOemPaidOnPasspointNetwork() { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() @@ -1307,7 +1308,7 @@ public class WifiNetworkSuggestionTest { */ @Test(expected = IllegalStateException.class) public void testSetCredentialSharedWithUserWithSetIsNetworkAsOemPaid() { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 2cf7f2c1b8cf..d0d0c57008fc 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -39,7 +39,6 @@ import android.content.pm.PackageManager; import android.net.MacAddress; import android.net.wifi.RttManager; import android.net.wifi.util.HexEncoding; -import android.net.wifi.util.SdkLevelUtil; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -48,6 +47,8 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -164,7 +165,7 @@ public class WifiAwareManagerTest { */ @Test public void testEnableInstantCommunicationMode() throws Exception { - assumeTrue(SdkLevelUtil.isAtLeastS()); + assumeTrue(SdkLevel.isAtLeastS()); mDut.isInstantCommunicationModeEnabled(); verify(mockAwareService).isInstantCommunicationModeEnabled(); mDut.enableInstantCommunicationMode(true); diff --git a/wifi/tests/test-jarjar-rules.txt b/wifi/tests/test-jarjar-rules.txt new file mode 100644 index 000000000000..41b97abb87b5 --- /dev/null +++ b/wifi/tests/test-jarjar-rules.txt @@ -0,0 +1 @@ +rule com.android.modules.utils.** com.android.wifi.test.x.@0 |