Merge changes from topics "mergedNetwork", "wifiInfoSubId"
* changes:
Add API to get Subscription Id
[Suggestion] API to set carrier merged network
diff --git a/Android.bp b/Android.bp
index 73bc382..6b39b51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -620,7 +620,6 @@
"av-types-aidl-java",
"mediatranscoding_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
- "modules-utils-os",
],
}
@@ -1260,6 +1259,7 @@
filegroup {
name: "framework-telephony-common-shared-srcs",
srcs: [
+ "core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/os/RegistrantList.java",
"core/java/android/os/Registrant.java",
"core/java/android/util/IndentingPrintWriter.java",
@@ -1342,6 +1342,7 @@
name: "framework-wifi-service-shared-srcs",
srcs: [
"core/java/android/net/InterfaceConfiguration.java",
+ "core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/util/BackupUtils.java",
"core/java/android/util/Rational.java",
"core/java/com/android/internal/util/FastXmlSerializer.java",
diff --git a/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java b/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java
index 2a538b2..e2c580c 100644
--- a/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/CharsetUtilsPerfTest.java
@@ -70,7 +70,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- CharsetUtils.toUtf8Bytes(mValue, destPtr, 0, dest.length);
+ CharsetUtils.toModifiedUtf8Bytes(mValue, destPtr, 0, dest.length);
}
}
@@ -85,7 +85,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- CharsetUtils.toUtf8Bytes(mValue, destPtr, 0, dest.length);
+ CharsetUtils.toModifiedUtf8Bytes(mValue, destPtr, 0, dest.length);
}
}
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index bf5781b..361325d 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -169,6 +169,7 @@
* @param tag Debugging tag for dumps associated with this job (instead of the service class)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public abstract @Result int scheduleAsPackage(@NonNull JobInfo job, @NonNull String packageName,
@@ -211,6 +212,7 @@
* Returns a list of all currently-executing jobs.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract List<JobInfo> getStartedJobs();
/**
@@ -220,5 +222,6 @@
* <p class="note">This is a slow operation, so it should be called sparingly.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract List<JobSnapshot> getAllJobSnapshots();
}
\ No newline at end of file
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 ba2a8a3..d4ea7af 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -439,7 +439,7 @@
private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1;
private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY;
- private static final boolean DEFAULT_LAZY_BATCHING = false;
+ private static final boolean DEFAULT_LAZY_BATCHING = true;
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index cc20213..1e72062 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -20,11 +20,10 @@
import android.app.AppGlobals;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.UserHandle;
-import com.android.modules.utils.BasicShellCommandHandler;
-
import java.io.PrintWriter;
public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
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 6f7dde2..1157ee9 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -2390,7 +2390,8 @@
false, this);
mInjector.registerDeviceConfigPropertiesChangedListener(this);
// Load all the constants.
- onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ // postOneTimeCheckIdleStates() doesn't need to be called on boot.
+ processProperties(mInjector.getDeviceConfigProperties());
updateSettings();
}
@@ -2402,6 +2403,11 @@
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ processProperties(properties);
+ postOneTimeCheckIdleStates();
+ }
+
+ private void processProperties(DeviceConfig.Properties properties) {
boolean timeThresholdsUpdated = false;
synchronized (mAppIdleLock) {
for (String name : properties.getKeyset()) {
@@ -2482,7 +2488,6 @@
}
}
}
- postOneTimeCheckIdleStates();
}
private void updateTimeThresholds() {
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index 71a52bb..e30df05 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -21,7 +21,7 @@
apex_defaults {
name: "com.android.permission-defaults",
updatable: true,
- min_sdk_version: "R",
+ min_sdk_version: "30",
key: "com.android.permission.key",
certificate: ":com.android.permission.certificate",
java_libs: [
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index ede8852..f13861e 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -35,7 +35,7 @@
prebuilts: ["com.android.os.statsd.init.rc"],
name: "com.android.os.statsd-defaults",
updatable: true,
- min_sdk_version: "R",
+ min_sdk_version: "30",
key: "com.android.os.statsd.key",
certificate: ":com.android.os.statsd.certificate",
}
diff --git a/api/current.txt b/api/current.txt
index 4f5e096..e24df42 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11744,7 +11744,7 @@
method public long getFirstInstallTime();
method public android.graphics.drawable.Drawable getIcon(int);
method public CharSequence getLabel();
- method public float getLoadingProgress();
+ method @FloatRange(from=0.0, to=1.0) public float getLoadingProgress();
method public String getName();
method public android.os.UserHandle getUser();
}
@@ -24192,6 +24192,25 @@
package android.media {
+ public final class ApplicationMediaCapabilities implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getSupportedHdrTypes();
+ method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes();
+ method public boolean isHdrTypeSupported(@NonNull String);
+ method public boolean isSlowMotionSupported();
+ method public boolean isVideoMimeTypeSupported(@NonNull String);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
+ }
+
+ public static final class ApplicationMediaCapabilities.Builder {
+ ctor public ApplicationMediaCapabilities.Builder();
+ method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedHdrType(@NonNull String);
+ method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedVideoMimeType(@NonNull String);
+ method @NonNull public android.media.ApplicationMediaCapabilities build();
+ method @NonNull public android.media.ApplicationMediaCapabilities.Builder setSlowMotionSupported(boolean);
+ }
+
public class AsyncPlayer {
ctor public AsyncPlayer(String);
method @Deprecated public void play(android.content.Context, android.net.Uri, boolean, int);
@@ -26313,6 +26332,17 @@
field public static final String TRACKS = "android.media.mediaextractor.ntrk";
}
+ public final class MediaFeature {
+ ctor public MediaFeature();
+ }
+
+ public static final class MediaFeature.HdrType {
+ field public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
+ field public static final String HDR10 = "android.media.feature.hdr.hdr10";
+ field public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
+ field public static final String HLG = "android.media.feature.hdr.hlg";
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
ctor public MediaFormat(@NonNull android.media.MediaFormat);
@@ -31682,6 +31712,7 @@
method public android.net.DhcpInfo getDhcpInfo();
method public int getMaxNumberOfNetworkSuggestionsPerApp();
method @IntRange(from=0) public int getMaxSignalLevel();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getNetworkSuggestionUserApprovalStatus();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public java.util.List<android.net.wifi.WifiNetworkSuggestion> getNetworkSuggestions();
method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", "android.permission.NETWORK_SETUP_WIZARD"}) public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
@@ -31752,6 +31783,11 @@
field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5
field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
+ field public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE = 4; // 0x4
+ field public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER = 2; // 0x2
+ field public static final int STATUS_SUGGESTION_APPROVAL_PENDING = 1; // 0x1
+ field public static final int STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER = 3; // 0x3
+ field public static final int STATUS_SUGGESTION_APPROVAL_UNKNOWN = 0; // 0x0
field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION = 1; // 0x1
field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION = 2; // 0x2
field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING = 3; // 0x3
@@ -53862,16 +53898,17 @@
field public int toolType;
}
- public interface OnReceiveContentCallback<T extends android.view.View> {
- method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
+ public interface OnReceiveContentListener {
+ method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.View, @NonNull android.view.OnReceiveContentListener.Payload);
}
- public static final class OnReceiveContentCallback.Payload {
+ public static final class OnReceiveContentListener.Payload {
method @NonNull public android.content.ClipData getClip();
method @Nullable public android.os.Bundle getExtras();
method public int getFlags();
method @Nullable public android.net.Uri getLinkUri();
method public int getSource();
+ method @NonNull public java.util.Map<java.lang.Boolean,android.view.OnReceiveContentListener.Payload> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>);
field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
field public static final int SOURCE_APP = 0; // 0x0
field public static final int SOURCE_AUTOFILL = 4; // 0x4
@@ -53881,12 +53918,15 @@
field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
}
- public static final class OnReceiveContentCallback.Payload.Builder {
- ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int);
- method @NonNull public android.view.OnReceiveContentCallback.Payload build();
- method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle);
- method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int);
- method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ public static final class OnReceiveContentListener.Payload.Builder {
+ ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.view.OnReceiveContentListener.Payload);
+ ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.content.ClipData, int);
+ method @NonNull public android.view.OnReceiveContentListener.Payload build();
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setClip(@NonNull android.content.ClipData);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setFlags(int);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setSource(int);
}
public abstract class OrientationEventListener {
@@ -54635,7 +54675,7 @@
method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
- method public boolean onReceiveContent(@NonNull android.view.OnReceiveContentCallback.Payload);
+ method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -54793,7 +54833,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
- method public void setOnReceiveContentCallback(@Nullable String[], @Nullable android.view.OnReceiveContentCallback);
+ method public void setOnReceiveContentListener(@Nullable String[], @Nullable android.view.OnReceiveContentListener);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -61810,11 +61850,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR;
}
- public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
- ctor public TextViewOnReceiveContentCallback();
- method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
- }
-
public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme();
method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index bdb8380..ebe6199 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -186,6 +186,8 @@
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
instrument.abi = nextArgRequired();
+ } else if (opt.equals("--no-restart")) {
+ instrument.noRestart = true;
} else {
System.err.println("Error: Unknown option: " + opt);
return;
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 7c30c8b..2be8264 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
@@ -89,6 +90,7 @@
public boolean disableTestApiChecks = true;
public boolean disableIsolatedStorage = false;
public String abi = null;
+ public boolean noRestart = false;
public int userId = UserHandle.USER_CURRENT;
public Bundle args = new Bundle();
// Required
@@ -514,6 +516,9 @@
if (disableIsolatedStorage) {
flags |= INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
}
+ if (noRestart) {
+ flags |= INSTR_FLAG_NO_RESTART;
+ }
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 0699ec0..39789cd 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -396,6 +396,23 @@
return true;
}
+bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes,
+ unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) {
+ // Share with metrics_manager_util.
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) {
+ return false;
+ }
+
+ for (const auto& [stateId, stateHash] : oldStateProtoHashes) {
+ const auto& it = newStateProtoHashes.find(stateId);
+ if (it != newStateProtoHashes.end() && it->second != stateHash) {
+ replacedStates.insert(stateId);
+ }
+ }
+ return true;
+}
// Returns true if any matchers in the metric activation were replaced.
bool metricActivationDepsChange(const StatsdConfig& config,
const unordered_map<int64_t, int>& metricToActivationMap,
@@ -1042,6 +1059,7 @@
set<int64_t>& noReportMetricIds) {
set<int64_t> replacedMatchers;
set<int64_t> replacedConditions;
+ set<int64_t> replacedStates;
set<int64_t> replacedMetrics;
vector<ConditionState> conditionCache;
unordered_map<int64_t, int> stateAtomIdMap;
@@ -1053,7 +1071,6 @@
ALOGE("updateAtomMatchingTrackers failed");
return false;
}
- VLOG("updateAtomMatchingTrackers succeeded");
if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers,
oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
@@ -1062,21 +1079,12 @@
ALOGE("updateConditions failed");
return false;
}
- VLOG("updateConditions succeeded");
- // Share with metrics_manager_util,
- if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) {
- ALOGE("initStates failed");
+ if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps,
+ newStateProtoHashes, replacedStates)) {
+ ALOGE("updateStates failed");
return false;
}
-
- set<int64_t> replacedStates;
- for (const auto& [stateId, stateHash] : oldStateProtoHashes) {
- const auto& it = newStateProtoHashes.find(stateId);
- if (it != newStateProtoHashes.end() && it->second != stateHash) {
- replacedStates.insert(stateId);
- }
- }
if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager,
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
index 178a9d2..8e2be68 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -126,6 +126,13 @@
std::vector<ConditionState>& conditionCache,
std::set<int64_t>& replacedConditions);
+bool updateStates(const StatsdConfig& config,
+ const std::map<int64_t, uint64_t>& oldStateProtoHashes,
+ std::unordered_map<int64_t, int>& stateAtomIdMap,
+ std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ std::map<int64_t, uint64_t>& newStateProtoHashes,
+ std::set<int64_t>& replacedStates);
+
// Function to determine the update status (preserve/replace/new) of all metrics in the config.
// [config]: the input StatsdConfig
// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 66bab4e..d78c14c 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -997,6 +997,57 @@
EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
}
+TEST_F(ConfigUpdateTest, TestUpdateStates) {
+ StatsdConfig config;
+ // Add states.
+ // Will be replaced because we add a state map.
+ State state1 = CreateScreenState();
+ int64_t state1Id = state1.id();
+ *config.add_state() = state1;
+
+ // Will be preserved.
+ State state2 = CreateUidProcessState();
+ int64_t state2Id = state2.id();
+ *config.add_state() = state2;
+
+ // Will be replaced since the atom changes from overlay to screen.
+ State state3 = CreateOverlayState();
+ int64_t state3Id = state3.id();
+ *config.add_state() = state3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change definitions of state1 and state3.
+ int64_t screenOnId = 0x4321, screenOffId = 0x1234;
+ *state1.mutable_map() = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId);
+ state3.set_atom_id(util::SCREEN_STATE_CHANGED);
+
+ StatsdConfig newConfig;
+ *newConfig.add_state() = state3;
+ *newConfig.add_state() = state1;
+ *newConfig.add_state() = state2;
+
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ map<int64_t, uint64_t> newStateProtoHashes;
+ set<int64_t> replacedStates;
+ EXPECT_TRUE(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps,
+ newStateProtoHashes, replacedStates));
+ EXPECT_THAT(replacedStates, ContainerEq(set({state1Id, state3Id})));
+
+ unordered_map<int64_t, int> expectedStateAtomIdMap = {
+ {state1Id, util::SCREEN_STATE_CHANGED},
+ {state2Id, util::UID_PROCESS_STATE_CHANGED},
+ {state3Id, util::SCREEN_STATE_CHANGED}};
+ EXPECT_THAT(stateAtomIdMap, ContainerEq(expectedStateAtomIdMap));
+
+ unordered_map<int64_t, unordered_map<int, int64_t>> expectedStateGroupMaps = {
+ {state1Id,
+ {{android::view::DisplayStateEnum::DISPLAY_STATE_OFF, screenOffId},
+ {android::view::DisplayStateEnum::DISPLAY_STATE_ON, screenOnId}}}};
+ EXPECT_THAT(allStateGroupMaps, ContainerEq(expectedStateGroupMaps));
+}
+
TEST_F(ConfigUpdateTest, TestEventMetricPreserve) {
StatsdConfig config;
AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
diff --git a/config/preloaded-classes b/config/preloaded-classes
index ecf11c2..f56656b 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4532,6 +4532,7 @@
android.os.BadParcelableException
android.os.BaseBundle$NoImagePreloadHolder
android.os.BaseBundle
+android.os.BasicShellCommandHandler
android.os.BatteryManager
android.os.BatteryManagerInternal
android.os.BatteryProperty$1
@@ -9557,7 +9558,6 @@
com.android.internal.widget.VerifyCredentialResponse
com.android.internal.widget.ViewClippingUtil$ClippingParameters
com.android.internal.widget.ViewClippingUtil
-com.android.module.utils.BasicShellCommandHandler
com.android.okhttp.Address
com.android.okhttp.AndroidShimResponseCache
com.android.okhttp.Authenticator
diff --git a/core/api/current.txt b/core/api/current.txt
index 0e461c5..4de91bb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11744,7 +11744,7 @@
method public long getFirstInstallTime();
method public android.graphics.drawable.Drawable getIcon(int);
method public CharSequence getLabel();
- method public float getLoadingProgress();
+ method @FloatRange(from=0.0, to=1.0) public float getLoadingProgress();
method public String getName();
method public android.os.UserHandle getUser();
}
@@ -24174,6 +24174,25 @@
package android.media {
+ public final class ApplicationMediaCapabilities implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getSupportedHdrTypes();
+ method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes();
+ method public boolean isHdrTypeSupported(@NonNull String);
+ method public boolean isSlowMotionSupported();
+ method public boolean isVideoMimeTypeSupported(@NonNull String);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
+ }
+
+ public static final class ApplicationMediaCapabilities.Builder {
+ ctor public ApplicationMediaCapabilities.Builder();
+ method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedHdrType(@NonNull String);
+ method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedVideoMimeType(@NonNull String);
+ method @NonNull public android.media.ApplicationMediaCapabilities build();
+ method @NonNull public android.media.ApplicationMediaCapabilities.Builder setSlowMotionSupported(boolean);
+ }
+
public class AsyncPlayer {
ctor public AsyncPlayer(String);
method @Deprecated public void play(android.content.Context, android.net.Uri, boolean, int);
@@ -26271,6 +26290,17 @@
field public static final String TRACKS = "android.media.mediaextractor.ntrk";
}
+ public final class MediaFeature {
+ ctor public MediaFeature();
+ }
+
+ public static final class MediaFeature.HdrType {
+ field public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
+ field public static final String HDR10 = "android.media.feature.hdr.hdr10";
+ field public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
+ field public static final String HLG = "android.media.feature.hdr.hlg";
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
ctor public MediaFormat(@NonNull android.media.MediaFormat);
@@ -51965,16 +51995,17 @@
field public int toolType;
}
- public interface OnReceiveContentCallback<T extends android.view.View> {
- method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
+ public interface OnReceiveContentListener {
+ method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.View, @NonNull android.view.OnReceiveContentListener.Payload);
}
- public static final class OnReceiveContentCallback.Payload {
+ public static final class OnReceiveContentListener.Payload {
method @NonNull public android.content.ClipData getClip();
method @Nullable public android.os.Bundle getExtras();
method public int getFlags();
method @Nullable public android.net.Uri getLinkUri();
method public int getSource();
+ method @NonNull public java.util.Map<java.lang.Boolean,android.view.OnReceiveContentListener.Payload> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>);
field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
field public static final int SOURCE_APP = 0; // 0x0
field public static final int SOURCE_AUTOFILL = 4; // 0x4
@@ -51984,12 +52015,15 @@
field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
}
- public static final class OnReceiveContentCallback.Payload.Builder {
- ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int);
- method @NonNull public android.view.OnReceiveContentCallback.Payload build();
- method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle);
- method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int);
- method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ public static final class OnReceiveContentListener.Payload.Builder {
+ ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.view.OnReceiveContentListener.Payload);
+ ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.content.ClipData, int);
+ method @NonNull public android.view.OnReceiveContentListener.Payload build();
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setClip(@NonNull android.content.ClipData);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setFlags(int);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setSource(int);
}
public abstract class OrientationEventListener {
@@ -52738,7 +52772,7 @@
method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
- method public boolean onReceiveContent(@NonNull android.view.OnReceiveContentCallback.Payload);
+ method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -52896,7 +52930,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
- method public void setOnReceiveContentCallback(@Nullable String[], @Nullable android.view.OnReceiveContentCallback);
+ method public void setOnReceiveContentListener(@Nullable String[], @Nullable android.view.OnReceiveContentListener);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -59913,11 +59947,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR;
}
- public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
- ctor public TextViewOnReceiveContentCallback();
- method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
- }
-
public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme();
method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 37a5389..8214e31 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11011,6 +11011,24 @@
package android.telephony.data {
+ public class ApnSetting implements android.os.Parcelable {
+ method public static int getApnTypeInt(@NonNull String);
+ method @NonNull public static String getApnTypeString(int);
+ field public static final String TYPE_ALL_STRING = "*";
+ field public static final String TYPE_CBS_STRING = "cbs";
+ field public static final String TYPE_DEFAULT_STRING = "default";
+ field public static final String TYPE_DUN_STRING = "dun";
+ field public static final String TYPE_EMERGENCY_STRING = "emergency";
+ field public static final String TYPE_FOTA_STRING = "fota";
+ field public static final String TYPE_HIPRI_STRING = "hipri";
+ field public static final String TYPE_IA_STRING = "ia";
+ field public static final String TYPE_IMS_STRING = "ims";
+ field public static final String TYPE_MCX_STRING = "mcx";
+ field public static final String TYPE_MMS_STRING = "mms";
+ field public static final String TYPE_SUPL_STRING = "supl";
+ field public static final String TYPE_XCAP_STRING = "xcap";
+ }
+
public final class DataCallResponse implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
@@ -11358,7 +11376,6 @@
method public int getEmergencyServiceCategories();
method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
- method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes();
method @NonNull public android.os.Bundle getProprietaryCallExtras();
method public int getRestrictCause();
method public int getServiceType();
@@ -11380,7 +11397,6 @@
method public void setEmergencyServiceCategories(int);
method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
method public void setHasKnownUserIntentEmergency(boolean);
- method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -11889,6 +11905,7 @@
public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
ctor public MmTelFeature();
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f17b815..1e6e784 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -173,6 +173,12 @@
*/
public static final int INSTR_FLAG_DISABLE_TEST_API_CHECKS = 1 << 2;
+ /**
+ * Do not restart the target process when starting or finishing instrumentation.
+ * @hide
+ */
+ public static final int INSTR_FLAG_NO_RESTART = 1 << 3;
+
static final class UidObserver extends IUidObserver.Stub {
final OnUidImportanceListener mListener;
final Context mContext;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0f6ebf2..2211807 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -381,6 +381,7 @@
String mInstrumentedAppDir = null;
String[] mInstrumentedSplitAppDirs = null;
String mInstrumentedLibDir = null;
+ boolean mInstrumentingWithoutRestart;
boolean mSystemThread = false;
boolean mSomeActivitiesChanged = false;
/* package */ boolean mHiddenApiWarningShown = false;
@@ -1774,6 +1775,19 @@
key.mLock.notifyAll();
}
}
+
+ @Override
+ public void instrumentWithoutRestart(ComponentName instrumentationName,
+ Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
+ IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) {
+ AppBindData data = new AppBindData();
+ data.instrumentationName = instrumentationName;
+ data.instrumentationArgs = instrumentationArgs;
+ data.instrumentationWatcher = instrumentationWatcher;
+ data.instrumentationUiAutomationConnection = instrumentationUiConnection;
+ data.appInfo = targetInfo;
+ sendMessage(H.INSTRUMENT_WITHOUT_RESTART, data);
+ }
}
private @NonNull SafeCancellationTransport createSafeCancellationTransport(
@@ -1879,6 +1893,9 @@
public static final int PURGE_RESOURCES = 161;
public static final int ATTACH_STARTUP_AGENTS = 162;
+ public static final int INSTRUMENT_WITHOUT_RESTART = 170;
+ public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
+
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
@@ -1921,6 +1938,9 @@
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PURGE_RESOURCES: return "PURGE_RESOURCES";
case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
+ case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
+ case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
+ return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
}
}
return Integer.toString(code);
@@ -2102,6 +2122,12 @@
case ATTACH_STARTUP_AGENTS:
handleAttachStartupAgents((String) msg.obj);
break;
+ case INSTRUMENT_WITHOUT_RESTART:
+ handleInstrumentWithoutRestart((AppBindData) msg.obj);
+ break;
+ case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
+ handleFinishInstrumentationWithoutRestart();
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -6500,32 +6526,7 @@
// setting up the app context.
final InstrumentationInfo ii;
if (data.instrumentationName != null) {
- try {
- ii = new ApplicationPackageManager(
- null, getPackageManager(), getPermissionManager())
- .getInstrumentationInfo(data.instrumentationName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(
- "Unable to find instrumentation info for: " + data.instrumentationName);
- }
-
- // Warn of potential ABI mismatches.
- if (!Objects.equals(data.appInfo.primaryCpuAbi, ii.primaryCpuAbi)
- || !Objects.equals(data.appInfo.secondaryCpuAbi, ii.secondaryCpuAbi)) {
- Slog.w(TAG, "Package uses different ABI(s) than its instrumentation: "
- + "package[" + data.appInfo.packageName + "]: "
- + data.appInfo.primaryCpuAbi + ", " + data.appInfo.secondaryCpuAbi
- + " instrumentation[" + ii.packageName + "]: "
- + ii.primaryCpuAbi + ", " + ii.secondaryCpuAbi);
- }
-
- mInstrumentationPackageName = ii.packageName;
- mInstrumentationAppDir = ii.sourceDir;
- mInstrumentationSplitAppDirs = ii.splitSourceDirs;
- mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
- mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
- mInstrumentedLibDir = data.info.getLibDir();
+ ii = prepareInstrumentation(data);
} else {
ii = null;
}
@@ -6554,48 +6555,7 @@
// Continue loading instrumentation.
if (ii != null) {
- ApplicationInfo instrApp;
- try {
- instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0,
- UserHandle.myUserId());
- } catch (RemoteException e) {
- instrApp = null;
- }
- if (instrApp == null) {
- instrApp = new ApplicationInfo();
- }
- ii.copyTo(instrApp);
- instrApp.initForUser(UserHandle.myUserId());
- final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
- appContext.getClassLoader(), false, true, false);
-
- // The test context's op package name == the target app's op package name, because
- // the app ops manager checks the op package name against the real calling UID,
- // which is what the target package name is associated with.
- final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
- appContext.getOpPackageName());
-
- try {
- final ClassLoader cl = instrContext.getClassLoader();
- mInstrumentation = (Instrumentation)
- cl.loadClass(data.instrumentationName.getClassName()).newInstance();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate instrumentation "
- + data.instrumentationName + ": " + e.toString(), e);
- }
-
- final ComponentName component = new ComponentName(ii.packageName, ii.name);
- mInstrumentation.init(this, instrContext, appContext, component,
- data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
-
- if (mProfiler.profileFile != null && !ii.handleProfiling
- && mProfiler.profileFd == null) {
- mProfiler.handlingProfiling = true;
- final File file = new File(mProfiler.profileFile);
- file.getParentFile().mkdirs();
- Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
- }
+ initInstrumentation(ii, data, appContext);
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
@@ -6686,6 +6646,120 @@
}
}
+ private void handleInstrumentWithoutRestart(AppBindData data) {
+ try {
+ data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+ mInstrumentingWithoutRestart = true;
+ final InstrumentationInfo ii = prepareInstrumentation(data);
+ final ContextImpl appContext =
+ ContextImpl.createAppContext(this, data.info);
+
+ initInstrumentation(ii, data, appContext);
+
+ try {
+ mInstrumentation.onCreate(data.instrumentationArgs);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Exception thrown in onCreate() of "
+ + data.instrumentationName + ": " + e.toString(), e);
+ }
+
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in handleInstrumentWithoutRestart", e);
+ }
+ }
+
+ private InstrumentationInfo prepareInstrumentation(AppBindData data) {
+ final InstrumentationInfo ii;
+ try {
+ ii = new ApplicationPackageManager(
+ null, getPackageManager(), getPermissionManager())
+ .getInstrumentationInfo(data.instrumentationName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(
+ "Unable to find instrumentation info for: " + data.instrumentationName);
+ }
+
+ // Warn of potential ABI mismatches.
+ if (!Objects.equals(data.appInfo.primaryCpuAbi, ii.primaryCpuAbi)
+ || !Objects.equals(data.appInfo.secondaryCpuAbi, ii.secondaryCpuAbi)) {
+ Slog.w(TAG, "Package uses different ABI(s) than its instrumentation: "
+ + "package[" + data.appInfo.packageName + "]: "
+ + data.appInfo.primaryCpuAbi + ", " + data.appInfo.secondaryCpuAbi
+ + " instrumentation[" + ii.packageName + "]: "
+ + ii.primaryCpuAbi + ", " + ii.secondaryCpuAbi);
+ }
+
+ mInstrumentationPackageName = ii.packageName;
+ mInstrumentationAppDir = ii.sourceDir;
+ mInstrumentationSplitAppDirs = ii.splitSourceDirs;
+ mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
+ mInstrumentedAppDir = data.info.getAppDir();
+ mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+ mInstrumentedLibDir = data.info.getLibDir();
+
+ return ii;
+ }
+
+ private void initInstrumentation(
+ InstrumentationInfo ii, AppBindData data, ContextImpl appContext) {
+ ApplicationInfo instrApp;
+ try {
+ instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ instrApp = null;
+ }
+ if (instrApp == null) {
+ instrApp = new ApplicationInfo();
+ }
+ ii.copyTo(instrApp);
+ instrApp.initForUser(UserHandle.myUserId());
+ final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+ appContext.getClassLoader(), false, true, false);
+
+ // The test context's op package name == the target app's op package name, because
+ // the app ops manager checks the op package name against the real calling UID,
+ // which is what the target package name is associated with.
+ final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
+ appContext.getOpPackageName());
+
+ try {
+ final ClassLoader cl = instrContext.getClassLoader();
+ mInstrumentation = (Instrumentation)
+ cl.loadClass(data.instrumentationName.getClassName()).newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Unable to instantiate instrumentation "
+ + data.instrumentationName + ": " + e.toString(), e);
+ }
+
+ final ComponentName component = new ComponentName(ii.packageName, ii.name);
+ mInstrumentation.init(this, instrContext, appContext, component,
+ data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
+
+ if (mProfiler.profileFile != null && !ii.handleProfiling
+ && mProfiler.profileFd == null) {
+ mProfiler.handlingProfiling = true;
+ final File file = new File(mProfiler.profileFile);
+ file.getParentFile().mkdirs();
+ Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+ }
+ }
+
+ private void handleFinishInstrumentationWithoutRestart() {
+ mInstrumentation.onDestroy();
+ mInstrumentationPackageName = null;
+ mInstrumentationAppDir = null;
+ mInstrumentationSplitAppDirs = null;
+ mInstrumentationLibDir = null;
+ mInstrumentedAppDir = null;
+ mInstrumentedSplitAppDirs = null;
+ mInstrumentedLibDir = null;
+ mInstrumentingWithoutRestart = false;
+ }
+
/*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
IActivityManager am = ActivityManager.getService();
if (mProfiler.profileFile != null && mProfiler.handlingProfiling
@@ -6699,6 +6773,9 @@
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
+ if (mInstrumentingWithoutRestart) {
+ sendMessage(H.FINISH_INSTRUMENTATION_WITHOUT_RESTART, null);
+ }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index dc9918a..22ca42e 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -150,4 +150,9 @@
in RemoteCallback resultCallback);
void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth,
int userId, boolean published);
+ void instrumentWithoutRestart(in ComponentName instrumentationName,
+ in Bundle instrumentationArgs,
+ IInstrumentationWatcher instrumentationWatcher,
+ IUiAutomationConnection instrumentationUiConnection,
+ in ApplicationInfo targetInfo);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 4eef616..6535387a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -102,7 +102,6 @@
public final class LoadedApk {
static final String TAG = "LoadedApk";
static final boolean DEBUG = false;
- private static final String PROPERTY_NAME_APPEND_NATIVE = "pi.append_native_lib_paths";
@UnsupportedAppUsage
private final ActivityThread mActivityThread;
@@ -926,7 +925,7 @@
needToSetupJitProfiles = true;
}
- if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
+ if (!libPaths.isEmpty()) {
// Temporarily disable logging of disk reads on the Looper thread as this is necessary
StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f09e752..16ae081 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6776,7 +6776,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public boolean isDeviceManaged() {
try {
return mService.hasDeviceOwner();
@@ -10487,7 +10487,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public @Nullable CharSequence getDeviceOwnerOrganizationName() {
try {
return mService.getDeviceOwnerOrganizationName();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 422d3f7..e35fb03 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -839,6 +839,7 @@
}
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
protected abstract IContentProvider acquireProvider(Context c, String name);
@@ -855,15 +856,19 @@
}
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean releaseProvider(IContentProvider icp);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean releaseUnstableProvider(IContentProvider icp);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void unstableProviderDied(IContentProvider icp);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 46d4f22..fb1366d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -811,6 +811,7 @@
* case {@link #getOpPackageName()} will be the name of the primary package in
* that process (so that app ops uid verification will work with the name).
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract String getBasePackageName();
@@ -927,6 +928,7 @@
* @see #MODE_PRIVATE
* @removed
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract SharedPreferences getSharedPreferences(File file, @PreferencesMode int mode);
/**
@@ -957,6 +959,7 @@
public abstract boolean deleteSharedPreferences(String name);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void reloadSharedPreferences();
/**
@@ -1045,6 +1048,7 @@
* @see #getSharedPreferences(String, int)
* @removed
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract File getSharedPreferencesPath(String name);
/**
@@ -1516,6 +1520,7 @@
* There is no guarantee when these files will be deleted.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@SystemApi
public abstract File getPreloadsFileCache();
@@ -2184,6 +2189,7 @@
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
String[] receiverPermissions);
@@ -2214,6 +2220,7 @@
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract void sendBroadcast(Intent intent,
@Nullable String receiverPermission,
@@ -2224,6 +2231,7 @@
* of an associated app op as per {@link android.app.AppOpsManager}.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void sendBroadcast(Intent intent,
String receiverPermission, int appOp);
@@ -2339,6 +2347,7 @@
* @see android.app.Activity#RESULT_OK
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract void sendOrderedBroadcast(@NonNull Intent intent,
@Nullable String receiverPermission, @Nullable Bundle options,
@@ -2351,6 +2360,7 @@
* of an associated app op as per {@link android.app.AppOpsManager}.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void sendOrderedBroadcast(Intent intent,
String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
@@ -2404,6 +2414,7 @@
* @see #sendBroadcast(Intent, String, Bundle)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
@@ -2426,6 +2437,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
@@ -2472,6 +2484,7 @@
* BroadcastReceiver, Handler, int, String, Bundle)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
@@ -2485,6 +2498,7 @@
* BroadcastReceiver, Handler, int, String, Bundle)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage
public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
@@ -2699,6 +2713,7 @@
* @hide
* This is just here for sending CONNECTIVITY_ACTION.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@RequiresPermission(allOf = {
android.Manifest.permission.INTERACT_ACROSS_USERS,
@@ -2989,6 +3004,7 @@
* @see #sendBroadcast
* @see #unregisterReceiver
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@UnsupportedAppUsage
@@ -3099,6 +3115,7 @@
/**
* @hide like {@link #startForegroundService(Intent)} but for a specific user.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user);
@@ -3137,6 +3154,7 @@
/**
* @hide like {@link #startService(Intent)} but for a specific user.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage
@@ -3145,6 +3163,7 @@
/**
* @hide like {@link #stopService(Intent)} but for a specific user.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
@@ -5282,6 +5301,7 @@
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@PackageManager.PermissionResult
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract int checkPermission(@NonNull String permission, int pid, int uid,
@@ -5509,6 +5529,7 @@
@Intent.AccessUriMode int modeFlags);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@PackageManager.PermissionResult
public abstract int checkUriPermission(Uri uri, int pid, int uid,
@Intent.AccessUriMode int modeFlags, IBinder callerToken);
@@ -5793,6 +5814,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract Context createApplicationContext(ApplicationInfo application,
@CreatePackageOptions int flags) throws PackageManager.NameNotFoundException;
@@ -6024,6 +6046,7 @@
* @see #isCredentialProtectedStorage()
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract Context createCredentialProtectedStorageContext();
@@ -6036,6 +6059,7 @@
* @return The compatibility info holder, or null if not required by the application.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
/**
@@ -6070,12 +6094,14 @@
* @see #getDisplay()
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@TestApi
public abstract int getDisplayId();
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void updateDisplay(int displayId);
/**
@@ -6104,6 +6130,7 @@
* @see #createCredentialProtectedStorageContext()
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract boolean isCredentialProtectedStorage();
@@ -6111,6 +6138,7 @@
* Returns true if the context can load unsafe resources, e.g. fonts.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract boolean canLoadUnsafeResources();
/**
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 7c48bdf..82d7b63 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.FloatRange;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -86,7 +87,7 @@
/**
* @return Package loading progress, range between [0, 1].
*/
- public float getLoadingProgress() {
+ public @FloatRange(from = 0.0, to = 1.0) float getLoadingProgress() {
return mInternal.getIncrementalStatesInfo().getProgress();
}
@@ -129,6 +130,16 @@
}
/**
+ * Returns the ActivityInfo of the activity.
+ *
+ * @return Activity Info
+ * @hide
+ */
+ public ActivityInfo getActivityInfo() {
+ return mInternal.getActivityInfo();
+ }
+
+ /**
* Returns the application info for the appliction this activity belongs to.
* @return
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 74370c3..cc484de 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3919,6 +3919,7 @@
* found on the system.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage
public abstract PackageInfo getPackageInfoAsUser(@NonNull String packageName,
@@ -3985,6 +3986,7 @@
* does not contain such an activity.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
/**
@@ -4050,6 +4052,7 @@
* found on the system.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract int getPackageUidAsUser(@NonNull String packageName, @UserIdInt int userId)
throws NameNotFoundException;
@@ -4068,6 +4071,7 @@
* found on the system.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract int getPackageUidAsUser(@NonNull String packageName,
@PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
@@ -4110,6 +4114,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract boolean arePermissionsIndividuallyControlled();
@@ -4118,6 +4123,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract boolean isWirelessConsentModeEnabled();
/**
@@ -4170,6 +4176,7 @@
@ApplicationInfoFlags int flags) throws NameNotFoundException;
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
@@ -4360,6 +4367,7 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@@ -4408,6 +4416,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
@NonNull
@TestApi
@@ -4521,6 +4530,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
public abstract void grantRuntimePermission(@NonNull String packageName,
@@ -4547,6 +4557,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
public abstract void revokeRuntimePermission(@NonNull String packageName,
@@ -4591,6 +4602,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
@@ -4613,6 +4625,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
@@ -4888,6 +4901,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
@@ -5003,6 +5017,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
@@ -5019,6 +5034,7 @@
* found on the system.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract int getUidForSharedUser(@NonNull String sharedUserName)
throws NameNotFoundException;
@@ -5062,6 +5078,7 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@TestApi
public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
@@ -5074,6 +5091,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
public abstract @NonNull List<InstantAppInfo> getInstantApps();
@@ -5085,6 +5103,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
public abstract @Nullable Drawable getInstantAppIcon(String packageName);
@@ -5133,6 +5152,7 @@
* deprecated
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract int getInstantAppCookieMaxSize();
/**
@@ -5190,6 +5210,7 @@
/**
* @removed
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract boolean setInstantAppCookie(@Nullable byte[] cookie);
/**
@@ -5228,6 +5249,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
@InstallFlags int flags, @UserIdInt int userId);
@@ -5240,6 +5262,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
@SystemApi
@@ -5259,6 +5282,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
@TestApi
public abstract @NonNull String getServicesSystemSharedLibraryPackageName();
@@ -5270,6 +5294,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
@TestApi
public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
@@ -5376,6 +5401,7 @@
* containing something else, such as the activity resolver.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
@@ -5417,6 +5443,7 @@
* empty list is returned.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
@@ -5440,6 +5467,7 @@
* empty list is returned.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
@@ -5502,6 +5530,7 @@
* no matching receivers, an empty list or null is returned.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@@ -5513,6 +5542,7 @@
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
@@ -5551,6 +5581,7 @@
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
@ResolveInfoFlags int flags, @UserIdInt int userId);
@@ -5583,6 +5614,7 @@
* empty list or null is returned.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
@@ -5621,6 +5653,7 @@
* no matching services, an empty list or null is returned.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
@@ -5688,6 +5721,7 @@
* provider. If a provider was not found, returns null.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
@@ -6072,6 +6106,7 @@
* @return the drawable or null if no drawable is required.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract Drawable getUserBadgeForDensity(@NonNull UserHandle user, int density);
@@ -6090,6 +6125,7 @@
* @return the drawable or null if no drawable is required.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract Drawable getUserBadgeForDensityNoBackground(@NonNull UserHandle user,
@@ -6228,6 +6264,7 @@
* TODO(b/170852794): mark maxTargetSdk as {@code Build.VERSION_CODES.S}
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170928809,
publicAlternatives = "Use {@code Context#createContextAsUser(UserHandle, int)}"
@@ -6280,6 +6317,7 @@
*
* @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@SystemApi
public abstract int installExistingPackage(@NonNull String packageName)
@@ -6292,6 +6330,7 @@
*
* @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@SystemApi
public abstract int installExistingPackage(@NonNull String packageName,
@@ -6304,6 +6343,7 @@
*
* @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@RequiresPermission(anyOf = {
Manifest.permission.INSTALL_EXISTING_PACKAGES,
@@ -6384,6 +6424,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT)
public abstract void verifyIntentFilter(int verificationId, int verificationCode,
@@ -6409,6 +6450,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
public abstract int getIntentVerificationStatusAsUser(@NonNull String packageName,
@@ -6434,6 +6476,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String packageName,
@@ -6451,6 +6494,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
@@ -6467,6 +6511,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
public abstract List<IntentFilter> getAllIntentFilters(@NonNull String packageName);
@@ -6481,6 +6526,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@@ -6498,6 +6544,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(allOf = {
Manifest.permission.SET_PREFERRED_APPLICATIONS,
@@ -6524,6 +6571,7 @@
@Nullable String installerPackageName);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
public abstract void setUpdateAvailable(@NonNull String packageName, boolean updateAvaialble);
@@ -6544,6 +6592,7 @@
* indicate that no callback is desired.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(Manifest.permission.DELETE_PACKAGES)
@UnsupportedAppUsage
public abstract void deletePackage(@NonNull String packageName,
@@ -6564,6 +6613,7 @@
* @param userId The user Id
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(anyOf = {
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.INTERACT_ACROSS_USERS_FULL})
@@ -6581,6 +6631,7 @@
*
* @deprecated use {@link #getInstallSourceInfo(String)} instead
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@Nullable
public abstract String getInstallerPackageName(@NonNull String packageName);
@@ -6620,6 +6671,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void clearApplicationUserData(@NonNull String packageName,
@Nullable IPackageDataObserver observer);
@@ -6639,6 +6691,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void deleteApplicationCacheFiles(@NonNull String packageName,
@Nullable IPackageDataObserver observer);
@@ -6661,6 +6714,7 @@
* callback is desired.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void deleteApplicationCacheFilesAsUser(@NonNull String packageName,
@UserIdInt int userId, @Nullable IPackageDataObserver observer);
@@ -6694,6 +6748,7 @@
}
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void freeStorageAndNotify(@Nullable String volumeUuid, long freeStorageSize,
@Nullable IPackageDataObserver observer);
@@ -6727,6 +6782,7 @@
}
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void freeStorage(@Nullable String volumeUuid, long freeStorageSize,
@Nullable IntentSender pi);
@@ -6750,6 +6806,7 @@
* @deprecated use {@link StorageStatsManager} instead.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@UnsupportedAppUsage
public abstract void getPackageSizeInfoAsUser(@NonNull String packageName,
@@ -6881,6 +6938,7 @@
* an app to be responsible for a particular role and to check current role
* holders, see {@link android.app.role.RoleManager}.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Deprecated
@UnsupportedAppUsage
public abstract void replacePreferredActivity(@NonNull IntentFilter filter, int match,
@@ -6988,6 +7046,7 @@
* default, if any.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract ComponentName getHomeActivities(@NonNull List<ResolveInfo> outActivities);
@@ -7089,6 +7148,7 @@
* @param userId Ther userId of the user whose restrictions are to be flushed.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void flushPackageRestrictionsAsUser(@UserIdInt int userId);
@@ -7099,6 +7159,7 @@
* or by installing it, such as with {@link #installExistingPackage(String)}
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean setApplicationHiddenSettingAsUser(@NonNull String packageName,
boolean hidden, @NonNull UserHandle userHandle);
@@ -7108,6 +7169,7 @@
* @see #setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean getApplicationHiddenSettingAsUser(@NonNull String packageName,
@NonNull UserHandle userHandle);
@@ -7141,6 +7203,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
public abstract void addOnPermissionsChangeListener(
@@ -7153,6 +7216,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
public abstract void removeOnPermissionsChangeListener(
@@ -7166,6 +7230,7 @@
* application's AndroidManifest.xml.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias);
@@ -7173,6 +7238,7 @@
/** Return the signing {@link KeySet} for this application.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract KeySet getSigningKeySet(@NonNull String packageName);
@@ -7184,6 +7250,7 @@
* Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean isSignedBy(@NonNull String packageName, @NonNull KeySet ks);
@@ -7193,6 +7260,7 @@
* {@link #isSignedBy(String packageName, KeySet ks)}.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean isSignedByExactly(@NonNull String packageName, @NonNull KeySet ks);
@@ -7410,6 +7478,7 @@
* @throws IllegalArgumentException if the package was not found.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean isPackageSuspendedForUser(@NonNull String packageName, int userId);
@@ -7485,6 +7554,7 @@
* @param packageName the package to change the category hint for.
* @param categoryHint the category hint to set.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void setApplicationCategoryHint(@NonNull String packageName,
@ApplicationInfo.Category int categoryHint);
@@ -7500,34 +7570,43 @@
}
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract int getMoveStatus(int moveId);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void registerMoveCallback(@NonNull MoveCallback callback,
@NonNull Handler handler);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void unregisterMoveCallback(@NonNull MoveCallback callback);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract int movePackage(@NonNull String packageName, @NonNull VolumeInfo vol);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract @Nullable VolumeInfo getPackageCurrentVolume(@NonNull ApplicationInfo app);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract List<VolumeInfo> getPackageCandidateVolumes(
@NonNull ApplicationInfo app);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract int movePrimaryStorage(@NonNull VolumeInfo vol);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume();
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes();
/**
@@ -7537,6 +7616,7 @@
* @return identity that uniquely identifies current device
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
@@ -7545,6 +7625,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean isUpgrade();
@@ -7574,6 +7655,7 @@
* {@link #ONLY_IF_NO_MATCH_FOUND}.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
@UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
@@ -7585,12 +7667,14 @@
* @param sourceUserId The source user id.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId);
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract Drawable loadItemIcon(@NonNull PackageItemInfo itemInfo,
@@ -7599,12 +7683,14 @@
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo,
@Nullable ApplicationInfo appInfo);
/** {@hide} */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean isPackageAvailable(@NonNull String packageName);
@@ -7825,6 +7911,7 @@
* user, {@code INSTALL_REASON_UNKNOWN} is returned.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@TestApi
@InstallReason
public abstract int getInstallReason(@NonNull String packageName, @NonNull UserHandle user);
@@ -7853,6 +7940,7 @@
* @see {@link android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS}
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@SystemApi
public abstract ComponentName getInstantAppResolverSettingsComponent();
@@ -7864,6 +7952,7 @@
* @see {@link android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE}
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
@SystemApi
public abstract ComponentName getInstantAppInstallerComponent();
@@ -7874,6 +7963,7 @@
* @see {@link android.provider.Settings.Secure#ANDROID_ID}
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@Nullable
public abstract String getInstantAppAndroidId(@NonNull String packageName,
@NonNull UserHandle user);
@@ -7918,6 +8008,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract void registerDexModule(@NonNull String dexModulePath,
@Nullable DexModuleRegisterCallback callback);
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index c5240c2..ddf3d90 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -419,7 +419,7 @@
return null;
}
- return generateActivityInfoUnchecked(a, applicationInfo);
+ return generateActivityInfoUnchecked(a, flags, applicationInfo);
}
/**
@@ -431,6 +431,7 @@
*/
@NonNull
public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
+ @PackageManager.ComponentInfoFlags int flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo();
@@ -463,7 +464,9 @@
ai.rotationAnimation = a.getRotationAnimation();
ai.colorMode = a.getColorMode();
ai.windowLayout = a.getWindowLayout();
- ai.metaData = a.getMetaData();
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ ai.metaData = a.getMetaData();
+ }
ai.applicationInfo = applicationInfo;
return ai;
}
@@ -489,7 +492,7 @@
return null;
}
- return generateServiceInfoUnchecked(s, applicationInfo);
+ return generateServiceInfoUnchecked(s, flags, applicationInfo);
}
/**
@@ -501,17 +504,20 @@
*/
@NonNull
public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
+ @PackageManager.ComponentInfoFlags int flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo();
assignSharedFieldsForComponentInfo(si, s);
si.exported = s.isExported();
si.flags = s.getFlags();
- si.metaData = s.getMetaData();
si.permission = s.getPermission();
si.processName = s.getProcessName();
si.mForegroundServiceType = s.getForegroundServiceType();
si.applicationInfo = applicationInfo;
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ si.metaData = s.getMetaData();
+ }
return si;
}
@@ -566,10 +572,12 @@
pi.initOrder = p.getInitOrder();
pi.uriPermissionPatterns = p.getUriPermissionPatterns();
pi.pathPermissions = p.getPathPermissions();
- pi.metaData = p.getMetaData();
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ pi.metaData = p.getMetaData();
+ }
pi.applicationInfo = applicationInfo;
return pi;
}
diff --git a/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl
index 7a006c3..492ceeb 100644
--- a/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl
@@ -28,4 +28,6 @@
void onDeviceCredentialPressed();
// Notifies the client that an internal event, e.g. back button has occurred.
void onSystemEvent(int event);
+ // Notifies that the dialog has finished animating in.
+ void onDialogAnimatedIn();
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index f0a83f0..e858159 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -188,6 +188,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void prepare(int maxCount, @NonNull Surface surface)
throws CameraAccessException;
@@ -227,6 +228,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void tearDown(@NonNull Surface surface) throws CameraAccessException;
/**
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index cd13707..37e1280 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -234,7 +234,7 @@
* @see StreamConfigurationMap#getOutputFormats()
* @see StreamConfigurationMap#getOutputSizes(int)
* @see StreamConfigurationMap#getOutputSizes(Class)
- * @deprecated Please use @{link
+ * @deprecated Please use {@link
* #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
* full set of configuration options available.
*/
@@ -249,7 +249,7 @@
*
* @see #createCaptureSession
* @see OutputConfiguration
- * @deprecated Please use @{link
+ * @deprecated Please use {@link
* #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
* full set of configuration options available.
*/
@@ -285,7 +285,7 @@
* @see StreamConfigurationMap#getOutputSizes
* @see android.media.ImageWriter
* @see android.media.ImageReader
- * @deprecated Please use @{link
+ * @deprecated Please use {@link
* #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
* full set of configuration options available.
*/
@@ -302,7 +302,7 @@
*
* @see #createReprocessableCaptureSession
* @see OutputConfiguration
- * @deprecated Please use @{link
+ * @deprecated Please use {@link
* #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
* full set of configuration options available.
*/
@@ -340,7 +340,7 @@
* @see CameraCaptureSession#captureBurst
* @see CameraCaptureSession#setRepeatingBurst
* @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
- * @deprecated Please use @{link
+ * @deprecated Please use {@link
* #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
* full set of configuration options available.
*/
@@ -413,7 +413,7 @@
* @see #createReprocessableCaptureSession
* @see CameraCaptureSession
* @see OutputConfiguration
- * @deprecated Please use @{link
+ * @deprecated Please use {@link
* #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
* full set of configuration options available.
* @hide
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 500b299..f1534d9 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -577,7 +577,7 @@
*/
@Nullable
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public HdmiClient getClient(int type) {
if (mService == null) {
return null;
@@ -610,7 +610,7 @@
*/
@Nullable
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public HdmiPlaybackClient getPlaybackClient() {
return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK);
}
@@ -628,7 +628,7 @@
*/
@Nullable
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public HdmiTvClient getTvClient() {
return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV);
}
@@ -646,7 +646,7 @@
* @hide
*/
@Nullable
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public HdmiAudioSystemClient getAudioSystemClient() {
return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
}
@@ -661,7 +661,7 @@
* @return {@link HdmiSwitchClient} instance. {@code null} on failure.
*/
@Nullable
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public HdmiSwitchClient getSwitchClient() {
return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1ed791d..d444807 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -654,7 +654,7 @@
* register a {@link android.hardware.location.ContextHubClientCallback}.
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public int registerCallback(@NonNull Callback callback) {
return registerCallback(callback, null);
}
@@ -688,7 +688,7 @@
* register a {@link android.hardware.location.ContextHubClientCallback}.
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public int registerCallback(Callback callback, Handler handler) {
synchronized(this) {
if (mCallback != null) {
@@ -892,7 +892,7 @@
* @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister
* a {@link android.hardware.location.ContextHubClientCallback}.
*/
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
@Deprecated
public int unregisterCallback(@NonNull Callback callback) {
synchronized(this) {
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 0edd055..969db96 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -257,6 +257,7 @@
* @throws IllegalArgumentException if id==0
* @hide This API is not thoroughly elaborated yet
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract @Nullable Bitmap getMetadataImage(int id);
/**
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 21634cc..1c35cb6 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -299,7 +299,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public boolean resetDevice() {
return native_reset_device();
}
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3ca5207..cd43637 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -202,6 +202,7 @@
* @param args The arguments passed to the dump method.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
abstract void dumpProtoInternal(FileDescriptor fd, String[] args);
/**
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7376830..4e019cf 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -301,7 +301,7 @@
* Changes only take effect during subsequent calls to
* {@link #tagSocket(Socket)}.
*/
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public static void setThreadStatsUid(int uid) {
NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
}
@@ -339,7 +339,7 @@
*
* @see #setThreadStatsUid(int)
*/
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public static void clearThreadStatsUid() {
NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
}
diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java
new file mode 100644
index 0000000..366da3d
--- /dev/null
+++ b/core/java/android/os/BasicShellCommandHandler.java
@@ -0,0 +1,342 @@
+/*
+ * 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.os;
+
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+/**
+ * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. This is meant to
+ * be copied into mainline modules, so this class must not use any hidden APIs.
+ *
+ * @hide
+ */
+public abstract class BasicShellCommandHandler {
+ static final String TAG = "ShellCommand";
+ static final boolean DEBUG = false;
+
+ private Binder mTarget;
+ private FileDescriptor mIn;
+ private FileDescriptor mOut;
+ private FileDescriptor mErr;
+ private String[] mArgs;
+
+ private String mCmd;
+ private int mArgPos;
+ private String mCurArgData;
+
+ private FileInputStream mFileIn;
+ private FileOutputStream mFileOut;
+ private FileOutputStream mFileErr;
+
+ private PrintWriter mOutPrintWriter;
+ private PrintWriter mErrPrintWriter;
+ private InputStream mInputStream;
+
+ public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, int firstArgPos) {
+ mTarget = target;
+ mIn = in;
+ mOut = out;
+ mErr = err;
+ mArgs = args;
+ mCmd = null;
+ mArgPos = firstArgPos;
+ mCurArgData = null;
+ mFileIn = null;
+ mFileOut = null;
+ mFileErr = null;
+ mOutPrintWriter = null;
+ mErrPrintWriter = null;
+ mInputStream = null;
+ }
+
+ public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args) {
+ String cmd;
+ int start;
+ if (args != null && args.length > 0) {
+ cmd = args[0];
+ start = 1;
+ } else {
+ cmd = null;
+ start = 0;
+ }
+ init(target, in, out, err, args, start);
+ mCmd = cmd;
+
+ if (DEBUG) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Log.d(TAG, "Starting command " + mCmd + " on " + mTarget, here);
+ Log.d(TAG, "Calling uid=" + Binder.getCallingUid()
+ + " pid=" + Binder.getCallingPid());
+ }
+ int res = -1;
+ try {
+ res = onCommand(mCmd);
+ if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget);
+ } catch (Throwable e) {
+ // Unlike usual calls, in this case if an exception gets thrown
+ // back to us we want to print it back in to the dump data, since
+ // that is where the caller expects all interesting information to
+ // go.
+ PrintWriter eout = getErrPrintWriter();
+ eout.println();
+ eout.println("Exception occurred while executing '" + mCmd + "':");
+ e.printStackTrace(eout);
+ } finally {
+ if (DEBUG) Log.d(TAG, "Flushing output streams on " + mTarget);
+ if (mOutPrintWriter != null) {
+ mOutPrintWriter.flush();
+ }
+ if (mErrPrintWriter != null) {
+ mErrPrintWriter.flush();
+ }
+ if (DEBUG) Log.d(TAG, "Sending command result on " + mTarget);
+ }
+ if (DEBUG) Log.d(TAG, "Finished command " + mCmd + " on " + mTarget);
+ return res;
+ }
+
+ /**
+ * Return the raw FileDescriptor for the output stream.
+ */
+ public FileDescriptor getOutFileDescriptor() {
+ return mOut;
+ }
+
+ /**
+ * Return direct raw access (not buffered) to the command's output data stream.
+ */
+ public OutputStream getRawOutputStream() {
+ if (mFileOut == null) {
+ mFileOut = new FileOutputStream(mOut);
+ }
+ return mFileOut;
+ }
+
+ /**
+ * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}.
+ */
+ public PrintWriter getOutPrintWriter() {
+ if (mOutPrintWriter == null) {
+ mOutPrintWriter = new PrintWriter(getRawOutputStream());
+ }
+ return mOutPrintWriter;
+ }
+
+ /**
+ * Return the raw FileDescriptor for the error stream.
+ */
+ public FileDescriptor getErrFileDescriptor() {
+ return mErr;
+ }
+
+ /**
+ * Return direct raw access (not buffered) to the command's error output data stream.
+ */
+ public OutputStream getRawErrorStream() {
+ if (mFileErr == null) {
+ mFileErr = new FileOutputStream(mErr);
+ }
+ return mFileErr;
+ }
+
+ /**
+ * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}.
+ */
+ public PrintWriter getErrPrintWriter() {
+ if (mErr == null) {
+ return getOutPrintWriter();
+ }
+ if (mErrPrintWriter == null) {
+ mErrPrintWriter = new PrintWriter(getRawErrorStream());
+ }
+ return mErrPrintWriter;
+ }
+
+ /**
+ * Return the raw FileDescriptor for the input stream.
+ */
+ public FileDescriptor getInFileDescriptor() {
+ return mIn;
+ }
+
+ /**
+ * Return direct raw access (not buffered) to the command's input data stream.
+ */
+ public InputStream getRawInputStream() {
+ if (mFileIn == null) {
+ mFileIn = new FileInputStream(mIn);
+ }
+ return mFileIn;
+ }
+
+ /**
+ * Return buffered access to the command's {@link #getRawInputStream()}.
+ */
+ public InputStream getBufferedInputStream() {
+ if (mInputStream == null) {
+ mInputStream = new BufferedInputStream(getRawInputStream());
+ }
+ return mInputStream;
+ }
+
+ /**
+ * Return the next option on the command line -- that is an argument that
+ * starts with '-'. If the next argument is not an option, null is returned.
+ */
+ public String getNextOption() {
+ if (mCurArgData != null) {
+ String prev = mArgs[mArgPos - 1];
+ throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
+ }
+ if (mArgPos >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mArgPos];
+ if (!arg.startsWith("-")) {
+ return null;
+ }
+ mArgPos++;
+ if (arg.equals("--")) {
+ return null;
+ }
+ if (arg.length() > 1 && arg.charAt(1) != '-') {
+ if (arg.length() > 2) {
+ mCurArgData = arg.substring(2);
+ return arg.substring(0, 2);
+ } else {
+ mCurArgData = null;
+ return arg;
+ }
+ }
+ mCurArgData = null;
+ return arg;
+ }
+
+ /**
+ * Return the next argument on the command line, whatever it is; if there are
+ * no arguments left, return null.
+ */
+ public String getNextArg() {
+ if (mCurArgData != null) {
+ String arg = mCurArgData;
+ mCurArgData = null;
+ return arg;
+ } else if (mArgPos < mArgs.length) {
+ return mArgs[mArgPos++];
+ } else {
+ return null;
+ }
+ }
+
+ public String peekNextArg() {
+ if (mCurArgData != null) {
+ return mCurArgData;
+ } else if (mArgPos < mArgs.length) {
+ return mArgs[mArgPos];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return all the remaining arguments in the command without moving the current position.
+ */
+ public String[] peekRemainingArgs() {
+ int remaining = getRemainingArgsCount();
+ String[] args = new String[remaining];
+ for (int pos = mArgPos; pos < mArgs.length; pos++) {
+ args[pos - mArgPos] = mArgs[pos];
+ }
+ return args;
+ }
+
+ /**
+ * Returns number of arguments that haven't been processed yet.
+ */
+ public int getRemainingArgsCount() {
+ if (mArgPos >= mArgs.length) {
+ return 0;
+ }
+ return mArgs.length - mArgPos;
+ }
+
+ /**
+ * Return the next argument on the command line, whatever it is; if there are
+ * no arguments left, throws an IllegalArgumentException to report this to the user.
+ */
+ public String getNextArgRequired() {
+ String arg = getNextArg();
+ if (arg == null) {
+ String prev = mArgs[mArgPos - 1];
+ throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
+ }
+ return arg;
+ }
+
+ public int handleDefaultCommands(String cmd) {
+ if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
+ onHelp();
+ } else {
+ getOutPrintWriter().println("Unknown command: " + cmd);
+ }
+ return -1;
+ }
+
+ public Binder getTarget() {
+ return mTarget;
+ }
+
+ public String[] getAllArgs() {
+ return mArgs;
+ }
+
+ /**
+ * Implement parsing and execution of a command. If it isn't a command you understand,
+ * call {@link #handleDefaultCommands(String)} and return its result as a last resort.
+ * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
+ * to process additional command line arguments. Command output can be written to
+ * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
+ *
+ * <p class="caution">Note that no permission checking has been done before entering this
+ * function, so you need to be sure to do your own security verification for any commands you
+ * are executing. The easiest way to do this is to have the ShellCommand contain
+ * only a reference to your service's aidl interface, and do all of your command
+ * implementations on top of that -- that way you can rely entirely on your executing security
+ * code behind that interface.</p>
+ *
+ * @param cmd The first command line argument representing the name of the command to execute.
+ * @return Return the command result; generally 0 or positive indicates success and
+ * negative values indicate error.
+ */
+ public abstract int onCommand(String cmd);
+
+ /**
+ * Implement this to print help text about your command to {@link #getOutPrintWriter()}.
+ */
+ public abstract void onHelp();
+}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 13b30f4..72cd8ba 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -422,7 +422,7 @@
* {@hide}
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
return verifyPackageCompatibility(inputStream);
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index a2173a6..3358ce1 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,9 +19,15 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Slog;
-import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.internal.util.FastPrintWriter;
+import java.io.BufferedInputStream;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
/**
* Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5d3c66c..cfc3e01 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2215,7 +2215,7 @@
/** @hide */
@SystemApi
@WorkerThread
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public long getAllocatableBytes(@NonNull UUID storageUuid,
@RequiresPermission @AllocateFlags int flags) throws IOException {
try {
@@ -2264,7 +2264,7 @@
/** @hide */
@SystemApi
@WorkerThread
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes,
@RequiresPermission @AllocateFlags int flags) throws IOException {
try {
@@ -2314,7 +2314,7 @@
/** @hide */
@SystemApi
@WorkerThread
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
@RequiresPermission @AllocateFlags int flags) throws IOException {
final File file = ParcelFileDescriptor.getFile(fd);
diff --git a/core/java/android/service/autofill/InternalTransformation.java b/core/java/android/service/autofill/InternalTransformation.java
index 0dba2b9..d31ea99 100644
--- a/core/java/android/service/autofill/InternalTransformation.java
+++ b/core/java/android/service/autofill/InternalTransformation.java
@@ -45,6 +45,7 @@
* @param template the {@link RemoteViews presentation template}.
* @param childViewId resource id of the child view inside the template.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
abstract void apply(@NonNull ValueFinder finder, @NonNull RemoteViews template,
int childViewId) throws Exception;
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 0bf68b7..8242f4e 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -90,7 +90,7 @@
*
* @param data the data to write
*/
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public int write(byte[] data) {
try {
return sService.write(data);
@@ -102,7 +102,7 @@
/**
* Returns the data block stored on the persistent partition.
*/
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public byte[] read() {
try {
return sService.read();
@@ -130,7 +130,7 @@
*
* Returns -1 on error.
*/
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public long getMaximumDataBlockSize() {
try {
return sService.getMaximumDataBlockSize();
diff --git a/core/java/android/util/CharsetUtils.java b/core/java/android/util/CharsetUtils.java
index 80c2055..fa14667 100644
--- a/core/java/android/util/CharsetUtils.java
+++ b/core/java/android/util/CharsetUtils.java
@@ -31,34 +31,48 @@
*/
public class CharsetUtils {
/**
- * Attempt to encode the given string as UTF-8 into the destination byte
- * array without making any new allocations.
+ * Attempt to encode the given string as modified UTF-8 into the destination
+ * byte array without making any new allocations.
*
* @param src string value to be encoded
* @param dest destination byte array to encode into
* @param destOff offset into destination where encoding should begin
* @param destLen length of destination
- * @return the number of bytes written to the destination when encoded
- * successfully, otherwise {@code -1} if not large enough
+ * @return positive value when encoding succeeded, or negative value when
+ * failed; the magnitude of the value is the number of bytes
+ * required to encode the string.
*/
- public static int toUtf8Bytes(@NonNull String src,
+ public static int toModifiedUtf8Bytes(@NonNull String src,
long dest, int destOff, int destLen) {
- return toUtf8Bytes(src, src.length(), dest, destOff, destLen);
+ return toModifiedUtf8Bytes(src, src.length(), dest, destOff, destLen);
}
/**
- * Attempt to encode the given string as UTF-8 into the destination byte
- * array without making any new allocations.
+ * Attempt to encode the given string as modified UTF-8 into the destination
+ * byte array without making any new allocations.
*
* @param src string value to be encoded
* @param srcLen exact length of string to be encoded
* @param dest destination byte array to encode into
* @param destOff offset into destination where encoding should begin
* @param destLen length of destination
- * @return the number of bytes written to the destination when encoded
- * successfully, otherwise {@code -1} if not large enough
+ * @return positive value when encoding succeeded, or negative value when
+ * failed; the magnitude of the value is the number of bytes
+ * required to encode the string.
*/
@FastNative
- private static native int toUtf8Bytes(@NonNull String src, int srcLen,
+ private static native int toModifiedUtf8Bytes(@NonNull String src, int srcLen,
long dest, int destOff, int destLen);
+
+ /**
+ * Attempt to decode a modified UTF-8 string from the source byte array.
+ *
+ * @param src source byte array to decode from
+ * @param srcOff offset into source where decoding should begin
+ * @param srcLen length of source that should be decoded
+ * @return the successfully decoded string
+ */
+ @FastNative
+ public static native @NonNull String fromModifiedUtf8Bytes(
+ long src, int srcOff, int srcLen);
}
diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java
deleted file mode 100644
index d74938c..0000000
--- a/core/java/android/view/OnReceiveContentCallback.java
+++ /dev/null
@@ -1,322 +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.view;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ClipData;
-import android.net.Uri;
-import android.os.Bundle;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Callback for apps to implement handling for insertion of content. Content may be both text and
- * non-text (plain/styled text, HTML, images, videos, audio files, etc).
- *
- * <p>This callback can be attached to different types of UI components using
- * {@link View#setOnReceiveContentCallback}.
- *
- * <p>For editable {@link android.widget.TextView} components, implementations can extend from
- * {@link android.widget.TextViewOnReceiveContentCallback} to reuse default platform behavior for
- * handling text.
- *
- * <p>Example implementation:<br>
- * <pre class="prettyprint">
- * // (1) Define the callback
- * public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
- * public static final Set<String> MIME_TYPES = Collections.unmodifiableSet(
- * Set.of("image/*", "video/*"));
- *
- * @Override
- * public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
- * // ... app-specific logic to handle the content in the payload ...
- * }
- * }
- *
- * // (2) Register the callback
- * public class MyActivity extends Activity {
- * @Override
- * public void onCreate(Bundle savedInstanceState) {
- * // ...
- *
- * EditText myInput = findViewById(R.id.my_input);
- * myInput.setOnReceiveContentCallback(
- * MyOnReceiveContentCallback.MIME_TYPES,
- * new MyOnReceiveContentCallback());
- * }
- * </pre>
- *
- * @param <T> The type of {@link View} with which this callback can be associated.
- */
-public interface OnReceiveContentCallback<T extends View> {
- /**
- * Receive the given content.
- *
- * <p>This method is only invoked for content whose MIME type matches a type specified via
- * {@link View#setOnReceiveContentCallback}.
- *
- * <p>For text, if the view has a selection, the selection should be overwritten by the clip; if
- * there's no selection, this method should insert the content at the current cursor position.
- *
- * <p>For non-text content (e.g. an image), the content may be inserted inline, or it may be
- * added as an attachment (could potentially be shown in a completely separate view).
- *
- * @param view The view where the content insertion was requested.
- * @param payload The content to insert and related metadata.
- *
- * @return Returns true if the content was handled in some way, false otherwise. Actual
- * insertion may be processed asynchronously in the background and may or may not succeed even
- * if this method returns true. For example, an app may not end up inserting an item if it
- * exceeds the app's size limit for that type of content.
- */
- boolean onReceiveContent(@NonNull T view, @NonNull Payload payload);
-
- /**
- * Holds all the relevant data for a request to {@link OnReceiveContentCallback}.
- */
- final class Payload {
-
- /**
- * Specifies the UI through which content is being inserted. Future versions of Android may
- * support additional values.
- *
- * @hide
- */
- @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
- SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Source {}
-
- /**
- * Specifies that the operation was triggered by the app that contains the target view.
- */
- public static final int SOURCE_APP = 0;
-
- /**
- * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
- * "Paste as plain text" action in the insertion/selection menu).
- */
- public static final int SOURCE_CLIPBOARD = 1;
-
- /**
- * Specifies that the operation was triggered from the soft keyboard (also known as input
- * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
- * for more info.
- */
- public static final int SOURCE_INPUT_METHOD = 2;
-
- /**
- * Specifies that the operation was triggered by the drag/drop framework. See
- * https://developer.android.com/guide/topics/ui/drag-drop for more info.
- */
- public static final int SOURCE_DRAG_AND_DROP = 3;
-
- /**
- * Specifies that the operation was triggered by the autofill framework. See
- * https://developer.android.com/guide/topics/text/autofill for more info.
- */
- public static final int SOURCE_AUTOFILL = 4;
-
- /**
- * Specifies that the operation was triggered by a result from a
- * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
- * menu.
- */
- public static final int SOURCE_PROCESS_TEXT = 5;
-
- /**
- * Returns the symbolic name of the given source.
- *
- * @hide
- */
- static String sourceToString(@Source int source) {
- switch (source) {
- case SOURCE_APP: return "SOURCE_APP";
- case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
- case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
- case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
- case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
- case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
- }
- return String.valueOf(source);
- }
-
- /**
- * Flags to configure the insertion behavior.
- *
- * @hide
- */
- @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Flags {}
-
- /**
- * Flag requesting that the content should be converted to plain text prior to inserting.
- */
- public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
-
- /**
- * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
- *
- * @hide
- */
- static String flagsToString(@Flags int flags) {
- if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
- return "FLAG_CONVERT_TO_PLAIN_TEXT";
- }
- return String.valueOf(flags);
- }
-
- @NonNull private final ClipData mClip;
- private final @Source int mSource;
- private final @Flags int mFlags;
- @Nullable private final Uri mLinkUri;
- @Nullable private final Bundle mExtras;
-
- private Payload(Builder b) {
- this.mClip = Objects.requireNonNull(b.mClip);
- this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT,
- "source");
- this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
- this.mLinkUri = b.mLinkUri;
- this.mExtras = b.mExtras;
- }
-
- @NonNull
- @Override
- public String toString() {
- return "Payload{"
- + "clip=" + mClip.getDescription()
- + ", source=" + sourceToString(mSource)
- + ", flags=" + flagsToString(mFlags)
- + ", linkUri=" + mLinkUri
- + ", extras=" + mExtras
- + "}";
- }
-
- /**
- * The data to be inserted.
- */
- public @NonNull ClipData getClip() {
- return mClip;
- }
-
- /**
- * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
- * may pass additional values.
- */
- public @Source int getSource() {
- return mSource;
- }
-
- /**
- * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
- */
- public @Flags int getFlags() {
- return mFlags;
- }
-
- /**
- * Optional http/https URI for the content that may be provided by the IME. This is only
- * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
- * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
- * IME.
- */
- public @Nullable Uri getLinkUri() {
- return mLinkUri;
- }
-
- /**
- * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
- * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
- * the IME.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Builder for {@link Payload}.
- */
- public static final class Builder {
- @NonNull private final ClipData mClip;
- private final @Source int mSource;
- private @Flags int mFlags;
- @Nullable private Uri mLinkUri;
- @Nullable private Bundle mExtras;
-
- /**
- * Creates a new builder.
- * @param clip The data to insert.
- * @param source The source of the operation. See {@code SOURCE_} constants.
- */
- public Builder(@NonNull ClipData clip, @Source int source) {
- mClip = clip;
- mSource = source;
- }
-
- /**
- * Sets flags that control content insertion behavior.
- * @param flags Optional flags to configure the insertion behavior. Use 0 for default
- * behavior. See {@code FLAG_} constants.
- * @return this builder
- */
- @NonNull
- public Builder setFlags(@Flags int flags) {
- mFlags = flags;
- return this;
- }
-
- /**
- * Sets the http/https URI for the content. See
- * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
- * @param linkUri Optional http/https URI for the content.
- * @return this builder
- */
- @NonNull
- public Builder setLinkUri(@Nullable Uri linkUri) {
- mLinkUri = linkUri;
- return this;
- }
-
- /**
- * Sets additional metadata.
- * @param extras Optional bundle with additional metadata.
- * @return this builder
- */
- @NonNull
- public Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * @return A new {@link Payload} instance with the data from this builder.
- */
- @NonNull
- public Payload build() {
- return new Payload(this);
- }
- }
- }
-}
diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java
new file mode 100644
index 0000000..4955289
--- /dev/null
+++ b/core/java/android/view/OnReceiveContentListener.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ * Listener for apps to implement handling for insertion of content. Content may be both text and
+ * non-text (plain/styled text, HTML, images, videos, audio files, etc).
+ *
+ * <p>This listener can be attached to different types of UI components using
+ * {@link View#setOnReceiveContentListener}.
+ *
+ * <p>Here is a sample implementation that handles content URIs and delegates the processing for
+ * text and everything else to the platform:<br>
+ * <pre class="prettyprint">
+ * // (1) Define the listener
+ * public class MyReceiver implements OnReceiveContentListener {
+ * public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
+ *
+ * @Override
+ * public Payload onReceiveContent(TextView view, Payload payload) {
+ * Map<Boolean, Payload> split = payload.partition(item -> item.getUri() != null);
+ * if (split.get(true) != null) {
+ * ClipData clip = payload.getClip();
+ * for (int i = 0; i < clip.getItemCount(); i++) {
+ * Uri uri = clip.getItemAt(i).getUri();
+ * // ... app-specific logic to handle the URI ...
+ * }
+ * }
+ * // Return anything that we didn't handle ourselves. This preserves the default platform
+ * // behavior for text and anything else for which we are not implementing custom handling.
+ * return split.get(false);
+ * }
+ * }
+ *
+ * // (2) Register the listener
+ * public class MyActivity extends Activity {
+ * @Override
+ * public void onCreate(Bundle savedInstanceState) {
+ * // ...
+ *
+ * EditText myInput = findViewById(R.id.my_input);
+ * myInput.setOnReceiveContentListener(MyReceiver.MIME_TYPES, new MyReceiver());
+ * }
+ * </pre>
+ */
+public interface OnReceiveContentListener {
+ /**
+ * Receive the given content.
+ *
+ * <p>Implementations should handle any content items of interest and return all unhandled
+ * items to preserve the default platform behavior for content that does not have app-specific
+ * handling. For example, an implementation may provide handling for content URIs (to provide
+ * support for inserting images, etc) and delegate the processing of text to the platform to
+ * preserve the common behavior for inserting text. See the class javadoc for a sample
+ * implementation and see {@link Payload#partition} for a convenient way to split the passed-in
+ * content.
+ *
+ * <p>If implementing handling for text: if the view has a selection, the selection should
+ * be overwritten by the passed-in content; if there's no selection, the passed-in content
+ * should be inserted at the current cursor position.
+ *
+ * <p>If implementing handling for non-text content (e.g. images): the content may be
+ * inserted inline, or it may be added as an attachment (could potentially be shown in a
+ * completely separate view).
+ *
+ * @param view The view where the content insertion was requested.
+ * @param payload The content to insert and related metadata.
+ *
+ * @return The portion of the passed-in content whose processing should be delegated to
+ * the platform. Return null if all content was handled in some way. Actual insertion of
+ * the content may be processed asynchronously in the background and may or may not
+ * succeed even if this method returns null. For example, an app may end up not inserting
+ * an item if it exceeds the app's size limit for that type of content.
+ */
+ @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload);
+
+ /**
+ * Holds all the relevant data for a request to {@link OnReceiveContentListener}.
+ */
+ final class Payload {
+
+ /**
+ * Specifies the UI through which content is being inserted. Future versions of Android may
+ * support additional values.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
+ SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Source {}
+
+ /**
+ * Specifies that the operation was triggered by the app that contains the target view.
+ */
+ public static final int SOURCE_APP = 0;
+
+ /**
+ * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
+ * "Paste as plain text" action in the insertion/selection menu).
+ */
+ public static final int SOURCE_CLIPBOARD = 1;
+
+ /**
+ * Specifies that the operation was triggered from the soft keyboard (also known as input
+ * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
+ * for more info.
+ */
+ public static final int SOURCE_INPUT_METHOD = 2;
+
+ /**
+ * Specifies that the operation was triggered by the drag/drop framework. See
+ * https://developer.android.com/guide/topics/ui/drag-drop for more info.
+ */
+ public static final int SOURCE_DRAG_AND_DROP = 3;
+
+ /**
+ * Specifies that the operation was triggered by the autofill framework. See
+ * https://developer.android.com/guide/topics/text/autofill for more info.
+ */
+ public static final int SOURCE_AUTOFILL = 4;
+
+ /**
+ * Specifies that the operation was triggered by a result from a
+ * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
+ * menu.
+ */
+ public static final int SOURCE_PROCESS_TEXT = 5;
+
+ /**
+ * Returns the symbolic name of the given source.
+ *
+ * @hide
+ */
+ static String sourceToString(@Source int source) {
+ switch (source) {
+ case SOURCE_APP: return "SOURCE_APP";
+ case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
+ case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
+ case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
+ case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
+ case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
+ }
+ return String.valueOf(source);
+ }
+
+ /**
+ * Flags to configure the insertion behavior.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /**
+ * Flag requesting that the content should be converted to plain text prior to inserting.
+ */
+ public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
+
+ /**
+ * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
+ *
+ * @hide
+ */
+ static String flagsToString(@Flags int flags) {
+ if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
+ return "FLAG_CONVERT_TO_PLAIN_TEXT";
+ }
+ return String.valueOf(flags);
+ }
+
+ @NonNull private final ClipData mClip;
+ private final @Source int mSource;
+ private final @Flags int mFlags;
+ @Nullable private final Uri mLinkUri;
+ @Nullable private final Bundle mExtras;
+
+ private Payload(Builder b) {
+ this.mClip = Objects.requireNonNull(b.mClip);
+ this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT,
+ "source");
+ this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
+ this.mLinkUri = b.mLinkUri;
+ this.mExtras = b.mExtras;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Payload{"
+ + "clip=" + mClip
+ + ", source=" + sourceToString(mSource)
+ + ", flags=" + flagsToString(mFlags)
+ + ", linkUri=" + mLinkUri
+ + ", extras=" + mExtras
+ + "}";
+ }
+
+ /**
+ * The data to be inserted.
+ */
+ public @NonNull ClipData getClip() {
+ return mClip;
+ }
+
+ /**
+ * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
+ * may pass additional values.
+ */
+ public @Source int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+ */
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Optional http/https URI for the content that may be provided by the IME. This is only
+ * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+ * IME.
+ */
+ public @Nullable Uri getLinkUri() {
+ return mLinkUri;
+ }
+
+ /**
+ * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+ * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+ * the IME.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Partitions this payload based on the given predicate.
+ *
+ * <p>Similar to a
+ * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector},
+ * this function classifies the content in this payload and organizes it into a map,
+ * grouping the content that matched vs didn't match the predicate.
+ *
+ * <p>Except for the {@link ClipData} items, the returned payloads will contain all the same
+ * metadata as the original payload.
+ *
+ * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which
+ * partition to place it into.
+ * @return A map containing the partitioned content. The map will contain a single entry if
+ * all items were classified into the same partition (all matched or all didn't match the
+ * predicate) or two entries (if there's at least one item that matched the predicate and at
+ * least one item that didn't match the predicate).
+ */
+ public @NonNull Map<Boolean, Payload> partition(
+ @NonNull Predicate<ClipData.Item> itemPredicate) {
+ if (mClip.getItemCount() == 1) {
+ Map<Boolean, Payload> result = new ArrayMap<>(1);
+ result.put(itemPredicate.test(mClip.getItemAt(0)), this);
+ return result;
+ }
+ ArrayList<ClipData.Item> accepted = new ArrayList<>();
+ ArrayList<ClipData.Item> remaining = new ArrayList<>();
+ for (int i = 0; i < mClip.getItemCount(); i++) {
+ ClipData.Item item = mClip.getItemAt(i);
+ if (itemPredicate.test(item)) {
+ accepted.add(item);
+ } else {
+ remaining.add(item);
+ }
+ }
+ Map<Boolean, Payload> result = new ArrayMap<>(2);
+ if (!accepted.isEmpty()) {
+ ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted);
+ result.put(true, new Builder(this).setClip(acceptedClip).build());
+ }
+ if (!remaining.isEmpty()) {
+ ClipData remainingClip = new ClipData(mClip.getDescription(), remaining);
+ result.put(false, new Builder(this).setClip(remainingClip).build());
+ }
+ return result;
+ }
+
+ /**
+ * Builder for {@link Payload}.
+ */
+ public static final class Builder {
+ @NonNull private ClipData mClip;
+ private @Source int mSource;
+ private @Flags int mFlags;
+ @Nullable private Uri mLinkUri;
+ @Nullable private Bundle mExtras;
+
+ /**
+ * Creates a new builder initialized with the data from the given builder.
+ */
+ public Builder(@NonNull Payload payload) {
+ mClip = payload.mClip;
+ mSource = payload.mSource;
+ mFlags = payload.mFlags;
+ mLinkUri = payload.mLinkUri;
+ mExtras = payload.mExtras;
+ }
+
+ /**
+ * Creates a new builder.
+ * @param clip The data to insert.
+ * @param source The source of the operation. See {@code SOURCE_} constants.
+ */
+ public Builder(@NonNull ClipData clip, @Source int source) {
+ mClip = clip;
+ mSource = source;
+ }
+
+ /**
+ * Sets the data to be inserted.
+ * @param clip The data to insert.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setClip(@NonNull ClipData clip) {
+ mClip = clip;
+ return this;
+ }
+
+ /**
+ * Sets the source of the operation.
+ * @param source The source of the operation. See {@code SOURCE_} constants.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setSource(@Source int source) {
+ mSource = source;
+ return this;
+ }
+
+ /**
+ * Sets flags that control content insertion behavior.
+ * @param flags Optional flags to configure the insertion behavior. Use 0 for default
+ * behavior. See {@code FLAG_} constants.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setFlags(@Flags int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Sets the http/https URI for the content. See
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
+ * @param linkUri Optional http/https URI for the content.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setLinkUri(@Nullable Uri linkUri) {
+ mLinkUri = linkUri;
+ return this;
+ }
+
+ /**
+ * Sets additional metadata.
+ * @param extras Optional bundle with additional metadata.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * @return A new {@link Payload} instance with the data from this builder.
+ */
+ @NonNull
+ public Payload build() {
+ return new Payload(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cefddd8..a88ad9f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -112,6 +112,7 @@
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.InputDevice.InputSourceClass;
+import android.view.OnReceiveContentListener.Payload;
import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation.Bounds;
@@ -143,6 +144,7 @@
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.view.ScrollCaptureInternal;
@@ -4713,6 +4715,9 @@
* Allows the application to implement custom scroll capture support.
*/
ScrollCaptureCallback mScrollCaptureCallback;
+
+ @Nullable
+ private OnReceiveContentListener mOnReceiveContentListener;
}
@UnsupportedAppUsage
@@ -5246,8 +5251,6 @@
@Nullable
private String[] mOnReceiveContentMimeTypes;
- @Nullable
- private OnReceiveContentCallback mOnReceiveContentCallback;
/**
* Simple constructor to use when creating a view from code.
@@ -9005,72 +9008,92 @@
}
/**
- * Sets the callback to handle insertion of content into this view.
+ * Sets the listener to be {@link #onReceiveContent used} to handle insertion of
+ * content into this view.
*
- * <p>Depending on the view, this callback may be invoked for scenarios such as content
- * insertion from the IME, Autofill, etc.
+ * <p>Depending on the type of view, this listener may be invoked for different scenarios. For
+ * example, for editable TextViews, this listener will be invoked for the following scenarios:
+ * <ol>
+ * <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
+ * insertion/selection menu)
+ * <li>Content insertion from the keyboard (from {@link InputConnection#commitContent})
+ * <li>Drag and drop (drop events from {@link #onDragEvent(DragEvent)})
+ * <li>Autofill
+ * <li>Selection replacement via {@link Intent#ACTION_PROCESS_TEXT}
+ * </ol>
*
- * <p>This callback is only invoked for content whose MIME type matches a type specified via
- * the {code mimeTypes} parameter. If the MIME type is not supported by the callback, the
- * default platform handling will be executed instead (no-op for the default {@link View}).
+ * <p>When setting a listener, clients should also declare the MIME types accepted by it.
+ * When invoked with other types of content, the listener may reject the content (defer to
+ * the default platform behavior) or execute some other fallback logic. The MIME types
+ * declared here allow different features to optionally alter their behavior. For example,
+ * the soft keyboard may choose to hide its UI for inserting GIFs for a particular input
+ * field if the MIME types set here for that field don't include "image/gif" or "image/*".
*
- * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
- * MIME types. As a result, you should always write your MIME types with lower case letters, or
- * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
- * case.</em>
+ * <p>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
+ * MIME types. As a result, you should always write your MIME types with lowercase letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
+ * lowercase.
*
- * @param mimeTypes The type of content for which the callback should be invoked. This may use
- * wildcards such as "text/*", "image/*", etc. This must not be null or empty if a non-null
- * callback is passed in.
- * @param callback The callback to use. This can be null to reset to the default behavior.
+ * @param mimeTypes The MIME types accepted by the given listener. These may use patterns
+ * such as "image/*", but may not start with a wildcard. This argument must
+ * not be null or empty if a non-null listener is passed in.
+ * @param listener The listener to use. This can be null to reset to the default behavior.
*/
@SuppressWarnings("rawtypes")
- public void setOnReceiveContentCallback(@Nullable String[] mimeTypes,
- @Nullable OnReceiveContentCallback callback) {
- if (callback != null) {
+ public void setOnReceiveContentListener(@Nullable String[] mimeTypes,
+ @Nullable OnReceiveContentListener listener) {
+ if (listener != null) {
Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0,
- "When the callback is set, MIME types must also be set");
+ "When the listener is set, MIME types must also be set");
}
- mOnReceiveContentMimeTypes = mimeTypes;
- mOnReceiveContentCallback = callback;
+ if (mimeTypes != null) {
+ Preconditions.checkArgument(Arrays.stream(mimeTypes).noneMatch(t -> t.startsWith("*")),
+ "A MIME type set here must not start with *: " + Arrays.toString(mimeTypes));
+ }
+ mOnReceiveContentMimeTypes = ArrayUtils.isEmpty(mimeTypes) ? null : mimeTypes;
+ getListenerInfo().mOnReceiveContentListener = listener;
}
/**
- * Receives the given content. The default implementation invokes the callback set via
- * {@link #setOnReceiveContentCallback}. If no callback is set or if the callback does not
- * support the given content (based on the MIME type), returns false.
+ * Receives the given content. Invokes the listener configured via
+ * {@link #setOnReceiveContentListener}; if no listener is set, the default implementation is a
+ * no-op (returns the passed-in content without acting on it).
*
* @param payload The content to insert and related metadata.
*
- * @return Returns true if the content was handled in some way, false otherwise. Actual
- * insertion may be processed asynchronously in the background and may or may not succeed even
- * if this method returns true. For example, an app may not end up inserting an item if it
- * exceeds the app's size limit for that type of content.
+ * @return The portion of the passed-in content that was not accepted (may be all, some, or none
+ * of the passed-in content).
*/
- public boolean onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
- ClipDescription description = payload.getClip().getDescription();
- if (mOnReceiveContentCallback != null && mOnReceiveContentMimeTypes != null
- && description.hasMimeType(mOnReceiveContentMimeTypes)) {
- return mOnReceiveContentCallback.onReceiveContent(this, payload);
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
+ final OnReceiveContentListener listener = (mListenerInfo == null) ? null
+ : getListenerInfo().mOnReceiveContentListener;
+ if (listener != null) {
+ return listener.onReceiveContent(this, payload);
}
- return false;
+ return payload;
}
/**
- * Returns the MIME types that can be handled by {@link #onReceiveContent} for this view, as
- * configured via {@link #setOnReceiveContentCallback}. By default returns null.
+ * Returns the MIME types accepted by {@link #onReceiveContent} for this view, as
+ * configured via {@link #setOnReceiveContentListener}. By default returns null.
*
- * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
- * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
- * soft keyboard may choose to hide its UI for inserting GIFs for a particular input field if
- * the MIME types returned here for that field don't include "image/gif".
+ * <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft
+ * keyboard, etc) may optionally use this metadata to conditionally alter their behavior. For
+ * example, a soft keyboard may choose to hide its UI for inserting GIFs for a particular
+ * input field if the MIME types returned here for that field don't include "image/gif" or
+ * "image/*".
*
* <p>Note: Comparisons of MIME types should be performed using utilities such as
* {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to
- * correctly handle patterns (e.g. "text/*").
+ * correctly handle patterns such as "text/*", "image/*", etc. Note that MIME type matching
+ * in the Android framework is case-sensitive, unlike formal RFC MIME types. As a result,
+ * you should always write your MIME types with lowercase letters, or use
+ * {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
+ * lowercase.
*
- * @return The MIME types supported by {@link #onReceiveContent} for this view. The returned
- * MIME types may contain wildcards such as "text/*", "image/*", etc.
+ * @return The MIME types accepted by {@link #onReceiveContent} for this view (may
+ * include patterns such as "image/*").
*/
public @Nullable String[] getOnReceiveContentMimeTypes() {
return mOnReceiveContentMimeTypes;
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 606e8f9..29ce231 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -97,6 +97,7 @@
public abstract void setVisibility(int visibility);
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void setAssistBlocked(boolean state);
/**
@@ -431,6 +432,7 @@
public abstract void asyncCommit();
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract Rect getTempRect();
/**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 8dd4b66..9c16378 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1387,6 +1387,7 @@
}
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void alwaysReadCloseOnTouchAttr();
@@ -1567,6 +1568,7 @@
*
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void clearContentView();
/**
@@ -2636,18 +2638,21 @@
* Called when the activity changes from fullscreen mode to multi-window mode and visa-versa.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void onMultiWindowModeChanged();
/**
* Called when the activity changes to/from picture-in-picture mode.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
/**
* Called when the activity just relaunched.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void reportActivityRelaunched();
/**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 81db628..299c41b 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -19,7 +19,7 @@
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.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
@@ -62,7 +62,7 @@
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.KeyEvent;
-import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentListener.Payload;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -2371,12 +2371,10 @@
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
+ Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
+ Payload result = view.onReceiveContent(payload);
+ if (result != null) {
+ Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id
+ ", view=" + view + ", clip=" + clip);
reportAutofillContentFailure(id);
return;
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 62b1b1f..a92d1f5 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -16,7 +16,7 @@
package android.view.inputmethod;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD;
import android.annotation.CallSuper;
import android.annotation.IntRange;
@@ -40,7 +40,7 @@
import android.util.LogPrinter;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentListener;
import android.view.View;
class ComposingText implements NoCopySpan {
@@ -928,17 +928,14 @@
/**
* Default implementation which invokes {@link View#onReceiveContent} on the target view if the
- * MIME type of the content matches one of the MIME types returned by
- * {@link View#getOnReceiveContentMimeTypes()}. If the MIME type of the content is not matched,
- * returns false without any side effects.
+ * view {@link View#getOnReceiveContentMimeTypes allows} content insertion; otherwise returns
+ * false without any side effects.
*/
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
ClipDescription description = inputContentInfo.getDescription();
- final String[] viewMimeTypes = mTargetView.getOnReceiveContentMimeTypes();
- if (viewMimeTypes == null || !description.hasMimeType(viewMimeTypes)) {
+ if (mTargetView.getOnReceiveContentMimeTypes() == null) {
if (DEBUG) {
- Log.d(TAG, "Can't insert content from IME; unsupported MIME type: content="
- + description + ", viewMimeTypes=" + viewMimeTypes);
+ Log.d(TAG, "Can't insert content from IME: content=" + description);
}
return false;
}
@@ -950,13 +947,13 @@
return false;
}
}
- final ClipData clip = new ClipData(description,
+ final ClipData clip = new ClipData(inputContentInfo.getDescription(),
new ClipData.Item(inputContentInfo.getContentUri()));
- final OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD)
+ final OnReceiveContentListener.Payload payload =
+ new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD)
.setLinkUri(inputContentInfo.getLinkUri())
.setExtras(opts)
.build();
- return mTargetView.onReceiveContent(payload);
+ return mTargetView.onReceiveContent(payload) == null;
}
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index f62a28e..023d9ff2 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -154,6 +154,7 @@
* HTTP request header
* @hide Used by Browser and by WebViewProvider implementations.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract String getCookie(String url, boolean privateBrowsing);
@@ -230,6 +231,7 @@
* @param privateBrowsing whether to use the private browsing cookie jar
* @hide Used by Browser and WebViewProvider implementations.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract boolean hasCookies(boolean privateBrowsing);
@@ -264,6 +266,7 @@
*
* @hide Only for use by WebViewProvider implementations
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
protected abstract boolean allowFileSchemeCookiesImpl();
@@ -299,6 +302,7 @@
*
* @hide Only for use by WebViewProvider implementations
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
protected abstract void setAcceptFileSchemeCookiesImpl(boolean accept);
}
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index b9e7042..2cb37b4 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -35,6 +35,7 @@
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract int getId();
diff --git a/core/java/android/webkit/WebIconDatabase.java b/core/java/android/webkit/WebIconDatabase.java
index 08956e0..b705658 100644
--- a/core/java/android/webkit/WebIconDatabase.java
+++ b/core/java/android/webkit/WebIconDatabase.java
@@ -75,6 +75,7 @@
/** {@hide}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract void bulkRequestIconForPageUrl(ContentResolver cr, String where,
IconListener listener);
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 91b9390..9b753f1 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -268,6 +268,7 @@
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract void setNavDump(boolean enabled);
@@ -280,6 +281,7 @@
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract boolean getNavDump();
@@ -457,6 +459,7 @@
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean view);
@@ -469,6 +472,7 @@
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract boolean getUseWebViewBackgroundForOverscrollBackground();
@@ -534,6 +538,7 @@
* Developers should access this via {@link CookieManager#setShouldAcceptThirdPartyCookies}.
* @hide Internal API.
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract void setAcceptThirdPartyCookies(boolean accept);
@@ -542,6 +547,7 @@
* Developers should access this via {@link CookieManager#getShouldAcceptThirdPartyCookies}.
* @hide Internal API
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract boolean getAcceptThirdPartyCookies();
@@ -669,6 +675,7 @@
* @deprecated Please use {@link #setUserAgentString} instead.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract void setUserAgent(int ua);
@@ -687,6 +694,7 @@
* @deprecated Please use {@link #getUserAgentString} instead.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract int getUserAgent();
@@ -1050,6 +1058,7 @@
* {@link #setPluginState}
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract void setPluginsEnabled(boolean flag);
@@ -1259,6 +1268,7 @@
* @deprecated This method has been replaced by {@link #getPluginState}
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@Deprecated
public abstract boolean getPluginsEnabled();
@@ -1445,6 +1455,7 @@
* WebView.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag);
@@ -1455,6 +1466,7 @@
* @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@SystemApi
public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled();
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 88c0809..e740cc2 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5348,6 +5348,7 @@
*
* @param down true if the scroll is going down, false if it is going up
*/
+ @SuppressWarnings("HiddenAbstractMethod")
abstract void fillGap(boolean down);
void hideSelector() {
@@ -5385,6 +5386,7 @@
* @param y Where the user touched
* @return The position of the first (or only) item in the row containing y
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
abstract int findMotionRow(int y);
@@ -5432,6 +5434,7 @@
*
* @param position the position of the new selection
*/
+ @SuppressWarnings("HiddenAbstractMethod")
abstract void setSelectionInt(int position);
/**
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index daf6914..76e97ad 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -310,6 +310,7 @@
}
}
+ @SuppressWarnings("HiddenAbstractMethod")
abstract void layout(int delta, boolean animate);
@Override
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index e36243c..da14f2c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,7 +16,7 @@
package android.widget;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP;
import android.R;
import android.animation.ValueAnimator;
@@ -98,7 +98,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentListener;
import android.view.SubMenu;
import android.view.View;
import android.view.View.DragShadowBuilder;
@@ -207,8 +207,8 @@
}
// Default content insertion handler.
- private final TextViewOnReceiveContentCallback mDefaultOnReceiveContentCallback =
- new TextViewOnReceiveContentCallback();
+ private final TextViewOnReceiveContentListener mDefaultOnReceiveContentListener =
+ new TextViewOnReceiveContentListener();
// Each Editor manages its own undo stack.
private final UndoManager mUndoManager = new UndoManager();
@@ -589,8 +589,8 @@
}
@VisibleForTesting
- public @NonNull TextViewOnReceiveContentCallback getDefaultOnReceiveContentCallback() {
- return mDefaultOnReceiveContentCallback;
+ public @NonNull TextViewOnReceiveContentListener getDefaultOnReceiveContentListener() {
+ return mDefaultOnReceiveContentListener;
}
/**
@@ -719,7 +719,7 @@
hideCursorAndSpanControllers();
stopTextActionModeWithPreservingSelection();
- mDefaultOnReceiveContentCallback.clearInputConnectionInfo();
+ mDefaultOnReceiveContentListener.clearInputConnectionInfo();
}
private void discardTextDisplayLists() {
@@ -2869,8 +2869,8 @@
final int originalLength = mTextView.getText().length();
Selection.setSelection((Spannable) mTextView.getText(), offset);
final ClipData clip = event.getClipData();
- final OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
+ final OnReceiveContentListener.Payload payload =
+ new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
.build();
mTextView.onReceiveContent(payload);
if (dragDropIntoItself) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3ac78ba..9485753 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,10 +17,10 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT;
+import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
@@ -154,7 +154,7 @@
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentListener.Payload;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewConfiguration;
@@ -2151,10 +2151,7 @@
if (result != null) {
if (isTextEditable()) {
ClipData clip = ClipData.newPlainText("", result);
- OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(
- clip, SOURCE_PROCESS_TEXT)
- .build();
+ Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
onReceiveContent(payload);
if (mEditor != null) {
mEditor.refreshTextActionMode();
@@ -11858,8 +11855,7 @@
+ " cannot be autofilled into " + this);
return;
}
- final OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+ final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
onReceiveContent(payload);
}
@@ -12926,8 +12922,7 @@
if (clip == null) {
return;
}
- final OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD)
+ final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD)
.setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
.build();
onReceiveContent(payload);
@@ -13717,7 +13712,8 @@
public void onInputConnectionOpenedInternal(@NonNull InputConnection ic,
@NonNull EditorInfo editorInfo, @Nullable Handler handler) {
if (mEditor != null) {
- mEditor.getDefaultOnReceiveContentCallback().setInputConnectionInfo(ic, editorInfo);
+ mEditor.getDefaultOnReceiveContentListener().setInputConnectionInfo(this, ic,
+ editorInfo);
}
}
@@ -13725,68 +13721,35 @@
@Override
public void onInputConnectionClosedInternal() {
if (mEditor != null) {
- mEditor.getDefaultOnReceiveContentCallback().clearInputConnectionInfo();
+ mEditor.getDefaultOnReceiveContentListener().clearInputConnectionInfo();
}
}
/**
- * Sets the callback to handle insertion of content into this view.
+ * Receives the given content. Clients wishing to provide custom behavior should configure a
+ * listener via {@link #setOnReceiveContentListener}.
*
- * <p>This callback will be invoked for the following scenarios:
- * <ol>
- * <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
- * insertion/selection menu)
- * <li>Content insertion from the keyboard (from {@link InputConnection#commitContent})
- * <li>Drag and drop (drop events from {@link #onDragEvent(DragEvent)})
- * <li>Autofill (from {@link #autofill(AutofillValue)})
- * <li>{@link Intent#ACTION_PROCESS_TEXT} replacement
- * </ol>
+ * <p>If a listener is set, invokes the listener. If the listener returns a non-null result,
+ * executes the default platform handling for the portion of the content returned by the
+ * listener.
*
- * <p>This callback is only invoked for content whose MIME type matches a type specified via
- * the {code mimeTypes} parameter. If the MIME type is not supported by the callback, the
- * default platform handling will be executed instead (no-op for the default {@link View}).
- *
- * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
- * MIME types. As a result, you should always write your MIME types with lower case letters, or
- * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
- * case.</em>
- *
- * @param mimeTypes The type of content for which the callback should be invoked. This may use
- * wildcards such as "text/*", "image/*", etc. This must not be null or empty if a non-null
- * callback is passed in.
- * @param callback The callback to use. This can be null to reset to the default behavior.
- */
- @SuppressWarnings("rawtypes")
- @Override
- public void setOnReceiveContentCallback(
- @Nullable String[] mimeTypes,
- @Nullable OnReceiveContentCallback callback) {
- super.setOnReceiveContentCallback(mimeTypes, callback);
- }
-
- /**
- * Receives the given content. The default implementation invokes the callback set via
- * {@link #setOnReceiveContentCallback}. If no callback is set or if the callback does not
- * support the given content (based on the MIME type), executes the default platform handling
- * (e.g. coerces content to text if the source is
- * {@link OnReceiveContentCallback.Payload#SOURCE_CLIPBOARD} and this is an editable
- * {@link TextView}).
+ * <p>If no listener is set, executes the default platform behavior. For non-editable TextViews
+ * the default behavior is a no-op (returns the passed-in content without acting on it). For
+ * editable TextViews the default behavior coerces all content to text and inserts into the
+ * view.
*
* @param payload The content to insert and related metadata.
*
- * @return Returns true if the content was handled in some way, false otherwise. Actual
- * insertion may be processed asynchronously in the background and may or may not succeed even
- * if this method returns true. For example, an app may not end up inserting an item if it
- * exceeds the app's size limit for that type of content.
+ * @return The portion of the passed-in content that was not handled (may be all, some, or none
+ * of the passed-in content).
*/
@Override
- public boolean onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
- if (super.onReceiveContent(payload)) {
- return true;
- } else if (mEditor != null) {
- return mEditor.getDefaultOnReceiveContentCallback().onReceiveContent(this, payload);
+ public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
+ Payload remaining = super.onReceiveContent(payload);
+ if (remaining != null && mEditor != null) {
+ return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, remaining);
}
- return false;
+ return remaining;
}
private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
diff --git a/core/java/android/widget/TextViewOnReceiveContentCallback.java b/core/java/android/widget/TextViewOnReceiveContentListener.java
similarity index 70%
rename from core/java/android/widget/TextViewOnReceiveContentCallback.java
rename to core/java/android/widget/TextViewOnReceiveContentListener.java
index 7ed70ec..7ef68ec 100644
--- a/core/java/android/widget/TextViewOnReceiveContentCallback.java
+++ b/core/java/android/widget/TextViewOnReceiveContentListener.java
@@ -17,12 +17,10 @@
package android.widget;
import static android.content.ContentResolver.SCHEME_CONTENT;
-import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
-
-import static java.util.Collections.singleton;
+import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,11 +37,10 @@
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
-import android.util.ArraySet;
import android.util.Log;
-import android.view.OnReceiveContentCallback;
-import android.view.OnReceiveContentCallback.Payload.Flags;
-import android.view.OnReceiveContentCallback.Payload.Source;
+import android.view.OnReceiveContentListener;
+import android.view.OnReceiveContentListener.Payload.Flags;
+import android.view.OnReceiveContentListener.Payload.Source;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -54,42 +51,38 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Set;
/**
- * Default implementation of {@link android.view.OnReceiveContentCallback} for editable
+ * Default implementation of {@link OnReceiveContentListener} for editable
* {@link TextView} components. This class handles insertion of text (plain text, styled text, HTML,
- * etc) but not images or other content. This class can be used as a base class for an
- * implementation of {@link android.view.OnReceiveContentCallback} for a {@link TextView}, to
- * provide consistent behavior for insertion of text.
+ * etc) but not images or other content.
+ *
+ * @hide
*/
-public class TextViewOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
+@VisibleForTesting
+public final class TextViewOnReceiveContentListener implements OnReceiveContentListener {
private static final String LOG_TAG = "OnReceiveContent";
- private static final String MIME_TYPE_ALL_TEXT = "text/*";
- private static final Set<String> MIME_TYPES_ALL_TEXT = singleton(MIME_TYPE_ALL_TEXT);
-
@Nullable private InputConnectionInfo mInputConnectionInfo;
- @Nullable private ArraySet<String> mCachedSupportedMimeTypes;
@Override
- public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
+ public @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
Log.d(LOG_TAG, "onReceive: " + payload);
}
- ClipData clip = payload.getClip();
- @Source int source = payload.getSource();
- @Flags int flags = payload.getFlags();
+ final @Source int source = payload.getSource();
if (source == SOURCE_INPUT_METHOD) {
// InputConnection.commitContent() should only be used for non-text input which is not
// supported by the default implementation.
- return false;
+ return payload;
}
if (source == SOURCE_AUTOFILL) {
- return onReceiveForAutofill(view, clip, flags);
+ onReceiveForAutofill((TextView) view, payload);
+ return null;
}
if (source == SOURCE_DRAG_AND_DROP) {
- return onReceiveForDragAndDrop(view, clip, flags);
+ onReceiveForDragAndDrop((TextView) view, payload);
+ return null;
}
// The code here follows the original paste logic from TextView:
@@ -97,7 +90,9 @@
// In particular, multiple items within the given ClipData will trigger separate calls to
// replace/insert. This is to preserve the original behavior with respect to TextWatcher
// notifications fired from SpannableStringBuilder when replace/insert is called.
- final Editable editable = (Editable) view.getText();
+ final ClipData clip = payload.getClip();
+ final @Flags int flags = payload.getFlags();
+ final Editable editable = (Editable) ((TextView) view).getText();
final Context context = view.getContext();
boolean didFirst = false;
for (int i = 0; i < clip.getItemCount(); i++) {
@@ -118,7 +113,7 @@
}
}
}
- return didFirst;
+ return null;
}
private static void replaceSelection(@NonNull Editable editable,
@@ -131,37 +126,33 @@
editable.replace(start, end, replacement);
}
- private boolean onReceiveForAutofill(@NonNull TextView view, @NonNull ClipData clip,
- @Flags int flags) {
+ private void onReceiveForAutofill(@NonNull TextView view, @NonNull Payload payload) {
+ ClipData clip = payload.getClip();
if (isUsageOfImeCommitContentEnabled(view)) {
clip = handleNonTextViaImeCommitContent(clip);
if (clip == null) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "onReceive: Handled via IME");
}
- return true;
+ return;
}
}
- final CharSequence text = coerceToText(clip, view.getContext(), flags);
+ final CharSequence text = coerceToText(clip, view.getContext(), payload.getFlags());
// First autofill it...
view.setText(text);
// ...then move cursor to the end.
final Editable editable = (Editable) view.getText();
Selection.setSelection(editable, editable.length());
- return true;
}
- private static boolean onReceiveForDragAndDrop(@NonNull TextView textView,
- @NonNull ClipData clip, @Flags int flags) {
- final CharSequence text = coerceToText(clip, textView.getContext(), flags);
- if (text.length() == 0) {
- return false;
- }
- replaceSelection((Editable) textView.getText(), text);
- return true;
+ private static void onReceiveForDragAndDrop(@NonNull TextView view, @NonNull Payload payload) {
+ final CharSequence text = coerceToText(payload.getClip(), view.getContext(),
+ payload.getFlags());
+ replaceSelection((Editable) view.getText(), text);
}
- private static CharSequence coerceToText(ClipData clip, Context context, @Flags int flags) {
+ private static @NonNull CharSequence coerceToText(@NonNull ClipData clip,
+ @NonNull Context context, @Flags int flags) {
SpannableStringBuilder ssb = new SpannableStringBuilder();
for (int i = 0; i < clip.getItemCount(); i++) {
CharSequence itemText;
@@ -183,17 +174,17 @@
* augmented autofill framework (see
* <a href="/guide/topics/text/autofill-services">autofill services</a>). In order for an app to
* be able to handle these suggestions, it must normally implement the
- * {@link android.view.OnReceiveContentCallback} API. To make the adoption of this smoother for
+ * {@link android.view.OnReceiveContentListener} API. To make the adoption of this smoother for
* apps that have previously implemented the
* {@link android.view.inputmethod.InputConnection#commitContent(InputContentInfo, int, Bundle)}
- * API, we reuse that API as a fallback if {@link android.view.OnReceiveContentCallback} is not
+ * API, we reuse that API as a fallback if {@link android.view.OnReceiveContentListener} is not
* yet implemented by the app. This fallback is only enabled on Android S. This change ID
* disables the fallback, such that apps targeting Android T and above must implement the
- * {@link android.view.OnReceiveContentCallback} API in order to accept non-text suggestions.
+ * {@link android.view.OnReceiveContentListener} API in order to accept non-text suggestions.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Enabled on Android T and higher
- private static final long AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK = 163400105L;
+ private static final long AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_LISTENER = 163400105L;
/**
* Returns true if we can use the IME {@link InputConnection#commitContent} API in order handle
@@ -206,7 +197,7 @@
}
return false;
}
- if (Compatibility.isChangeEnabled(AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK)) {
+ if (Compatibility.isChangeEnabled(AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_LISTENER)) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Fallback to commitContent disabled (target SDK is above S)");
}
@@ -238,11 +229,16 @@
* Invoked by the platform when an {@link InputConnection} is successfully created for the view
* that owns this callback instance.
*/
- void setInputConnectionInfo(@NonNull InputConnection ic, @NonNull EditorInfo editorInfo) {
+ void setInputConnectionInfo(@NonNull TextView view, @NonNull InputConnection ic,
+ @NonNull EditorInfo editorInfo) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "setInputConnectionInfo: "
+ Arrays.toString(editorInfo.contentMimeTypes));
}
+ if (!isUsageOfImeCommitContentEnabled(view)) {
+ mInputConnectionInfo = null;
+ return;
+ }
String[] contentMimeTypes = editorInfo.contentMimeTypes;
if (contentMimeTypes == null || contentMimeTypes.length == 0) {
mInputConnectionInfo = null;
@@ -262,82 +258,26 @@
mInputConnectionInfo = null;
}
- // TODO(b/168253885): Use this to populate the assist structure for Autofill
-
/** @hide */
@VisibleForTesting
- public Set<String> getMimeTypes(TextView view) {
+ @Nullable
+ public String[] getEditorInfoMimeTypes(@NonNull TextView view) {
if (!isUsageOfImeCommitContentEnabled(view)) {
- return MIME_TYPES_ALL_TEXT;
+ return null;
}
- return getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes();
- }
-
- private Set<String> getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes() {
- InputConnectionInfo icInfo = mInputConnectionInfo;
+ final InputConnectionInfo icInfo = mInputConnectionInfo;
if (icInfo == null) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
- Log.v(LOG_TAG, "getSupportedMimeTypes: No usable EditorInfo/InputConnection");
+ Log.v(LOG_TAG, "getEditorInfoMimeTypes: No usable EditorInfo");
}
- return MIME_TYPES_ALL_TEXT;
+ return null;
}
- String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes;
+ final String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes;
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
- Log.v(LOG_TAG, "getSupportedMimeTypes: Augmenting with EditorInfo.contentMimeTypes: "
+ Log.v(LOG_TAG, "getEditorInfoMimeTypes: "
+ Arrays.toString(editorInfoContentMimeTypes));
}
- ArraySet<String> supportedMimeTypes = mCachedSupportedMimeTypes;
- if (canReuse(supportedMimeTypes, editorInfoContentMimeTypes)) {
- return supportedMimeTypes;
- }
- supportedMimeTypes = new ArraySet<>(editorInfoContentMimeTypes);
- supportedMimeTypes.add(MIME_TYPE_ALL_TEXT);
- mCachedSupportedMimeTypes = supportedMimeTypes;
- return supportedMimeTypes;
- }
-
- /**
- * We want to avoid creating a new set on every invocation of
- * {@link #getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes()}.
- * This method will check if the cached set of MIME types matches the data in the given array
- * from {@link EditorInfo} or if a new set should be created. The custom logic is needed for
- * comparing the data because the set contains the additional "text/*" MIME type.
- *
- * @param cachedMimeTypes Previously cached set of MIME types.
- * @param newEditorInfoMimeTypes MIME types from {@link EditorInfo}.
- *
- * @return Returns true if the data in the given cached set matches the data in the array.
- *
- * @hide
- */
- @VisibleForTesting
- public static boolean canReuse(@Nullable ArraySet<String> cachedMimeTypes,
- @NonNull String[] newEditorInfoMimeTypes) {
- if (cachedMimeTypes == null) {
- return false;
- }
- if (newEditorInfoMimeTypes.length != cachedMimeTypes.size()
- && newEditorInfoMimeTypes.length != (cachedMimeTypes.size() - 1)) {
- return false;
- }
- final boolean ignoreAllTextMimeType =
- newEditorInfoMimeTypes.length == (cachedMimeTypes.size() - 1);
- for (String mimeType : cachedMimeTypes) {
- if (ignoreAllTextMimeType && mimeType.equals(MIME_TYPE_ALL_TEXT)) {
- continue;
- }
- boolean present = false;
- for (String editorInfoContentMimeType : newEditorInfoMimeTypes) {
- if (editorInfoContentMimeType.equals(mimeType)) {
- present = true;
- break;
- }
- }
- if (!present) {
- return false;
- }
- }
- return true;
+ return editorInfoContentMimeTypes;
}
/**
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index bb7e2af..fcaa963 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -426,7 +426,7 @@
}
public String getName() {
- return "Cuj<" + getCuj() + ">";
+ return "Cuj<" + getNameOfCuj(mCujType) + ">";
}
}
}
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c85b5d7..c110b26 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -18,10 +18,9 @@
package com.android.internal.os;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.BasicShellCommandHandler;
import android.os.Build;
-import com.android.modules.utils.BasicShellCommandHandler;
-
import java.io.PrintStream;
public abstract class BaseCommand {
diff --git a/core/java/com/android/internal/util/FastDataInput.java b/core/java/com/android/internal/util/FastDataInput.java
index 2e8cb47..f8d241b 100644
--- a/core/java/com/android/internal/util/FastDataInput.java
+++ b/core/java/com/android/internal/util/FastDataInput.java
@@ -17,6 +17,9 @@
package com.android.internal.util;
import android.annotation.NonNull;
+import android.util.CharsetUtils;
+
+import dalvik.system.VMRuntime;
import java.io.BufferedInputStream;
import java.io.Closeable;
@@ -25,7 +28,6 @@
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
@@ -39,9 +41,11 @@
public class FastDataInput implements DataInput, Closeable {
private static final int MAX_UNSIGNED_SHORT = 65_535;
+ private final VMRuntime mRuntime;
private final InputStream mIn;
private final byte[] mBuffer;
+ private final long mBufferPtr;
private final int mBufferCap;
private int mBufferPos;
@@ -54,12 +58,14 @@
private String[] mStringRefs = new String[32];
public FastDataInput(@NonNull InputStream in, int bufferSize) {
+ mRuntime = VMRuntime.getRuntime();
mIn = Objects.requireNonNull(in);
if (bufferSize < 8) {
throw new IllegalArgumentException();
}
- mBuffer = new byte[bufferSize];
+ mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize);
+ mBufferPtr = mRuntime.addressOf(mBuffer);
mBufferCap = mBuffer.length;
}
@@ -123,15 +129,15 @@
// Attempt to read directly from buffer space if there's enough room,
// otherwise fall back to chunking into place
final int len = readUnsignedShort();
- if (mBufferCap >= len) {
+ if (mBufferCap > len) {
if (mBufferLim - mBufferPos < len) fill(len);
- final String res = new String(mBuffer, mBufferPos, len, StandardCharsets.UTF_8);
+ final String res = CharsetUtils.fromModifiedUtf8Bytes(mBufferPtr, mBufferPos, len);
mBufferPos += len;
return res;
} else {
- final byte[] tmp = new byte[len];
- readFully(tmp, 0, tmp.length);
- return new String(tmp, StandardCharsets.UTF_8);
+ final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
+ readFully(tmp, 0, len);
+ return CharsetUtils.fromModifiedUtf8Bytes(mRuntime.addressOf(tmp), 0, len);
}
}
diff --git a/core/java/com/android/internal/util/FastDataOutput.java b/core/java/com/android/internal/util/FastDataOutput.java
index 2530501..83d26e1 100644
--- a/core/java/com/android/internal/util/FastDataOutput.java
+++ b/core/java/com/android/internal/util/FastDataOutput.java
@@ -28,7 +28,6 @@
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Objects;
@@ -42,6 +41,7 @@
public class FastDataOutput implements DataOutput, Flushable, Closeable {
private static final int MAX_UNSIGNED_SHORT = 65_535;
+ private final VMRuntime mRuntime;
private final OutputStream mOut;
private final byte[] mBuffer;
@@ -56,13 +56,14 @@
private HashMap<String, Short> mStringRefs = new HashMap<>();
public FastDataOutput(@NonNull OutputStream out, int bufferSize) {
+ mRuntime = VMRuntime.getRuntime();
mOut = Objects.requireNonNull(out);
if (bufferSize < 8) {
throw new IllegalArgumentException();
}
- mBuffer = (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, bufferSize);
- mBufferPtr = VMRuntime.getRuntime().addressOf(mBuffer);
+ mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize);
+ mBufferPtr = mRuntime.addressOf(mBuffer);
mBufferCap = mBuffer.length;
}
@@ -111,21 +112,28 @@
// Attempt to write directly to buffer space if there's enough room,
// otherwise fall back to chunking into place
if (mBufferCap - mBufferPos < 2 + s.length()) drain();
- final int res = CharsetUtils.toUtf8Bytes(s, mBufferPtr, mBufferPos + 2,
+
+ // Magnitude of this returned value indicates the number of bytes
+ // required to encode the string; sign indicates success/failure
+ int len = CharsetUtils.toModifiedUtf8Bytes(s, mBufferPtr, mBufferPos + 2,
mBufferCap - mBufferPos - 2);
- if (res >= 0) {
- if (res > MAX_UNSIGNED_SHORT) {
- throw new IOException("UTF-8 length too large: " + res);
- }
- writeShort(res);
- mBufferPos += res;
+ if (Math.abs(len) > MAX_UNSIGNED_SHORT) {
+ throw new IOException("Modified UTF-8 length too large: " + len);
+ }
+
+ if (len >= 0) {
+ // Positive value indicates the string was encoded into the buffer
+ // successfully, so we only need to prefix with length
+ writeShort(len);
+ mBufferPos += len;
} else {
- final byte[] tmp = s.getBytes(StandardCharsets.UTF_8);
- if (tmp.length > MAX_UNSIGNED_SHORT) {
- throw new IOException("UTF-8 length too large: " + res);
- }
- writeShort(tmp.length);
- write(tmp, 0, tmp.length);
+ // Negative value indicates buffer was too small and we need to
+ // allocate a temporary buffer for encoding
+ len = -len;
+ final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
+ CharsetUtils.toModifiedUtf8Bytes(s, mRuntime.addressOf(tmp), 0, tmp.length);
+ writeShort(len);
+ write(tmp, 0, len);
}
}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index c7ac189..a87e8aa 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -139,6 +139,38 @@
BackgroundThread.getHandler().post(this::readSettings);
}
+ /**
+ * A helper method to translate action type to name.
+ *
+ * @param action the action type defined in AtomsProto.java
+ * @return the name of the action
+ */
+ public static String getNameOfAction(int action) {
+ // Defined in AtomsProto.java
+ switch (action) {
+ case 0:
+ return "UNKNOWN";
+ case 1:
+ return "ACTION_EXPAND_PANEL";
+ case 2:
+ return "ACTION_TOGGLE_RECENTS";
+ case 3:
+ return "ACTION_FINGERPRINT_WAKE_AND_UNLOCK";
+ case 4:
+ return "ACTION_CHECK_CREDENTIAL";
+ case 5:
+ return "ACTION_CHECK_CREDENTIAL_UNLOCKED";
+ case 6:
+ return "ACTION_TURN_ON_SCREEN";
+ case 7:
+ return "ACTION_ROTATE_SCREEN";
+ case 8:
+ return "ACTION_FACE_WAKE_AND_UNLOCK";
+ default:
+ throw new IllegalArgumentException("Invalid action");
+ }
+ }
+
private void registerSettingsObserver() {
Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER);
mContext.getContentResolver().registerContentObserver(
diff --git a/core/jni/android_util_CharsetUtils.cpp b/core/jni/android_util_CharsetUtils.cpp
index 3e1d4a7..7ab6e8f2 100644
--- a/core/jni/android_util_CharsetUtils.cpp
+++ b/core/jni/android_util_CharsetUtils.cpp
@@ -19,13 +19,14 @@
namespace android {
-static jint android_util_CharsetUtils_toUtf8Bytes(JNIEnv *env, jobject clazz,
+static jint android_util_CharsetUtils_toModifiedUtf8Bytes(JNIEnv *env, jobject clazz,
jstring src, jint srcLen, jlong dest, jint destOff, jint destLen) {
char *destPtr = reinterpret_cast<char*>(dest);
// Quickly check if destination has plenty of room for worst-case
// 4-bytes-per-char encoded size
- if (destOff >= 0 && destOff + (srcLen * 4) < destLen) {
+ const size_t worstLen = (srcLen * 4);
+ if (destOff >= 0 && destOff + worstLen < destLen) {
env->GetStringUTFRegion(src, 0, srcLen, destPtr + destOff);
return strlen(destPtr + destOff + srcLen) + srcLen;
}
@@ -38,13 +39,29 @@
return encodedLen;
}
- return -1;
+ return -encodedLen;
+}
+
+static jstring android_util_CharsetUtils_fromModifiedUtf8Bytes(JNIEnv *env, jobject clazz,
+ jlong src, jint srcOff, jint srcLen) {
+ char *srcPtr = reinterpret_cast<char*>(src);
+
+ // This is funky, but we need to temporarily swap a null byte so that
+ // JNI knows where the string ends; we'll put it back, we promise
+ char tmp = srcPtr[srcOff + srcLen];
+ srcPtr[srcOff + srcLen] = '\0';
+ jstring res = env->NewStringUTF(srcPtr + srcOff);
+ srcPtr[srcOff + srcLen] = tmp;
+ return res;
}
static const JNINativeMethod methods[] = {
// @FastNative
- {"toUtf8Bytes", "(Ljava/lang/String;IJII)I",
- (void*)android_util_CharsetUtils_toUtf8Bytes},
+ {"toModifiedUtf8Bytes", "(Ljava/lang/String;IJII)I",
+ (void*)android_util_CharsetUtils_toModifiedUtf8Bytes},
+ // @FastNative
+ {"fromModifiedUtf8Bytes", "(JII)Ljava/lang/String;",
+ (void*)android_util_CharsetUtils_fromModifiedUtf8Bytes},
};
int register_android_util_CharsetUtils(JNIEnv *env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fbc5287..ea667277 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -668,6 +668,10 @@
<!-- For tether entitlement recheck-->
<protected-broadcast
android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
+
+ <!-- Made protected in S (was added in R) -->
+ <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
diff --git a/core/res/res/values-mcc311-mnc180-as/strings.xml b/core/res/res/values-mcc311-mnc180-as/strings.xml
new file mode 100644
index 0000000..daa090c
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-as/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"ছিমৰ অনুমতি নাই MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ফ\'নৰ অনুমতি নাই MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-az/strings.xml b/core/res/res/values-mcc311-mnc180-az/strings.xml
new file mode 100644
index 0000000..7b8bd89
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-az/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM MM#2 təmin etmir"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM MM#3 dəstəkləmir"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"MM#6 telefonu dəstəklənmir"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml b/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..6cbc6ee
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-b+sr+Latn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM kartica nije podešena MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM kartica nije dozvoljena MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Telefon nije dozvoljen MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-be/strings.xml b/core/res/res/values-mcc311-mnc180-be/strings.xml
new file mode 100644
index 0000000..363bcde
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-be/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM-карты няма MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM-карта не дапускаецца MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Тэлефон не дапускаецца MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-bn/strings.xml b/core/res/res/values-mcc311-mnc180-bn/strings.xml
new file mode 100644
index 0000000..23b9fdf
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-bn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"সিমের জন্য প্রস্তুত নয় MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"সিমের অনুমতি নেই MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ফোন অনুমোদিত নয় MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-bs/strings.xml b/core/res/res/values-mcc311-mnc180-bs/strings.xml
new file mode 100644
index 0000000..c73e0dc
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-bs/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM kartica nije dodijeljena MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM kartica nije dozvoljena MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Telefon nije dozvoljen MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-en-rAU/strings.xml b/core/res/res/values-mcc311-mnc180-en-rAU/strings.xml
new file mode 100644
index 0000000..bd58d57
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-en-rAU/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM not provisioned MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM not allowed MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Phone not allowed MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-en-rCA/strings.xml b/core/res/res/values-mcc311-mnc180-en-rCA/strings.xml
new file mode 100644
index 0000000..bd58d57
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-en-rCA/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM not provisioned MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM not allowed MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Phone not allowed MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-en-rXC/strings.xml b/core/res/res/values-mcc311-mnc180-en-rXC/strings.xml
new file mode 100644
index 0000000..a9fdf61
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-en-rXC/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM not provisioned MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM not allowed MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Phone not allowed MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-gu/strings.xml b/core/res/res/values-mcc311-mnc180-gu/strings.xml
new file mode 100644
index 0000000..26f735b
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-gu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIMને MM#2ની જોગવાઈ નથી"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIMને MM#3 કરવાની મંજૂરી નથી"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"MM#6 ફોનની મંજૂરી નથી"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-kk/strings.xml b/core/res/res/values-mcc311-mnc180-kk/strings.xml
new file mode 100644
index 0000000..18c492d
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-kk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM картасы қарастырылмаған MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM картасына рұқсат етілмеген MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Телефон пайдалануға болмайды MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-kn/strings.xml b/core/res/res/values-mcc311-mnc180-kn/strings.xml
new file mode 100644
index 0000000..6de3655
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-kn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"MM#2 ಗೆ ಸಿಮ್ ಸಿದ್ಧವಾಗಿಲ್ಲ"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"ಸಿಮ್ MM#3 ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ಫೋನ್ MM#6 ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-or/strings.xml b/core/res/res/values-mcc311-mnc180-or/strings.xml
new file mode 100644
index 0000000..8b2ffd9
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-or/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM କାର୍ଡ ପ୍ରସ୍ତୁତ କରାଯାଇନାହିଁ MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM କାର୍ଡର ଅନୁମତି ନାହିଁ MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ଫୋନ୍ର ଅନୁମତି ନାହିଁ MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-pa/strings.xml b/core/res/res/values-mcc311-mnc180-pa/strings.xml
new file mode 100644
index 0000000..b716a2e
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-pa/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"ਸਿਮ ਦੀ ਵਿਵਸਥਾ ਨਹੀਂ ਹੈ MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"ਸਿਮ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ਫ਼ੋਨ ਨੂੰ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml b/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml
new file mode 100644
index 0000000..6788df9
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"Chip não permitido MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Smartphone não permitido MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-sq/strings.xml b/core/res/res/values-mcc311-mnc180-sq/strings.xml
new file mode 100644
index 0000000..eeec051
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-sq/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"Karta SIM nuk është dhënë MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"Karta SIM nuk lejohet MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Telefoni nuk lejohet MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-ta/strings.xml b/core/res/res/values-mcc311-mnc180-ta/strings.xml
new file mode 100644
index 0000000..5a9f97b
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-ta/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"சிம் அமைக்கப்படவில்லை MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"சிம் அனுமதிக்கப்படவில்லை MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ஃபோன் அனுமதிக்கப்படவில்லை MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-te/strings.xml b/core/res/res/values-mcc311-mnc180-te/strings.xml
new file mode 100644
index 0000000..7368dae3
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-te/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM MM#2ని సక్రియం చేయలేదు"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM MM#3ని అనుమతించలేదు"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"ఫోన్ అనుమతించబడదు MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-ur/strings.xml b/core/res/res/values-mcc311-mnc180-ur/strings.xml
new file mode 100644
index 0000000..57695ec
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-ur/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM فراہم کردہ نہیں ہے MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM کی اجازت نہیں ہے MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"فون کی اجازت نہیں ہے MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mcc311-mnc180-uz/strings.xml b/core/res/res/values-mcc311-mnc180-uz/strings.xml
new file mode 100644
index 0000000..bb1f84a
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc180-uz/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"SIM karta ishlatish taqiqlangan (MM#2)"</string>
+ <string name="mmcc_illegal_ms" msgid="4073997279280371621">"SIM karta ishlatish taqiqlangan (MM#3)"</string>
+ <string name="mmcc_illegal_me" msgid="4936539345546223576">"Chaqiruvlar taqiqlangan (MM#6)"</string>
+</resources>
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 7766b57..5871e2e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -672,5 +672,11 @@
public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth,
int userId, boolean published) {
}
+
+ @Override
+ public void instrumentWithoutRestart(ComponentName instrumentationName,
+ Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
+ IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) {
+ }
}
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index cfed2ce..5fa8c4f 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.graphics.fonts.SystemFonts;
+import android.os.SharedMemory;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -35,9 +37,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.List;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Random;
@RunWith(AndroidJUnit4.class)
@@ -54,6 +56,10 @@
Typeface.create(Typeface.MONOSPACE, 0)
};
+ private static final int[] STYLES = {
+ Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC,
+ };
+
@SmallTest
@Test
public void testBasic() throws Exception {
@@ -66,6 +72,16 @@
@SmallTest
@Test
+ public void testDefaults() {
+ for (int style : STYLES) {
+ String msg = "style = " + style;
+ assertNotNull(msg, Typeface.defaultFromStyle(style));
+ assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle());
+ }
+ }
+
+ @SmallTest
+ @Test
public void testUnique() throws Exception {
final int n = mFaces.length;
for (int i = 0; i < n; i++) {
@@ -178,21 +194,57 @@
@SmallTest
@Test
public void testSerialize() throws Exception {
- int size = Typeface.writeTypefaces(null, Arrays.asList(mFaces));
- ByteBuffer buffer = ByteBuffer.allocateDirect(size);
- Typeface.writeTypefaces(buffer, Arrays.asList(mFaces));
- List<Typeface> copiedTypefaces = Typeface.readTypefaces(buffer);
- assertNotNull(copiedTypefaces);
- assertEquals(mFaces.length, copiedTypefaces.size());
- for (int i = 0; i < mFaces.length; i++) {
- Typeface original = mFaces[i];
- Typeface copied = copiedTypefaces.get(i);
+ HashMap<String, Typeface> systemFontMap = new HashMap<>();
+ Typeface.initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
+ SystemFonts.getAliases());
+ SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap);
+ Map<String, Typeface> copiedFontMap =
+ Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN));
+ assertEquals(systemFontMap.size(), copiedFontMap.size());
+ for (String key : systemFontMap.keySet()) {
+ assertTrue(copiedFontMap.containsKey(key));
+ Typeface original = systemFontMap.get(key);
+ Typeface copied = copiedFontMap.get(key);
assertEquals(original.getStyle(), copied.getStyle());
assertEquals(original.getWeight(), copied.getWeight());
assertEquals(measureText(original, "hello"), measureText(copied, "hello"), 1e-6);
}
}
+ @SmallTest
+ @Test
+ public void testSetSystemFontMap() throws Exception {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ Resources res = context.getResources();
+ Map<String, Typeface> fontMap = Map.of(
+ "sans-serif", Typeface.create(res.getFont(R.font.samplefont), Typeface.NORMAL),
+ "serif", Typeface.create(res.getFont(R.font.samplefont2), Typeface.NORMAL),
+ "monospace", Typeface.create(res.getFont(R.font.samplefont3), Typeface.NORMAL),
+ "sample", Typeface.create(res.getFont(R.font.samplefont4), Typeface.NORMAL),
+ "sample-italic", Typeface.create(res.getFont(R.font.samplefont4), Typeface.ITALIC));
+ Typeface.setSystemFontMap(fontMap);
+
+ // Test public static final fields
+ assertEquals(fontMap.get("sans-serif"), Typeface.DEFAULT);
+ assertEquals(Typeface.BOLD, Typeface.DEFAULT_BOLD.getStyle());
+ assertEquals(fontMap.get("sans-serif"), Typeface.SANS_SERIF);
+ assertEquals(fontMap.get("serif"), Typeface.SERIF);
+ assertEquals(fontMap.get("monospace"), Typeface.MONOSPACE);
+
+ // Test defaults
+ assertEquals(fontMap.get("sans-serif"), Typeface.defaultFromStyle(Typeface.NORMAL));
+ for (int style : STYLES) {
+ String msg = "style = " + style;
+ assertNotNull(msg, Typeface.defaultFromStyle(style));
+ assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle());
+ }
+
+ // Test create()
+ assertEquals(fontMap.get("sample"), Typeface.create("sample", Typeface.NORMAL));
+ assertEquals(
+ fontMap.get("sample-italic"), Typeface.create("sample-italic", Typeface.ITALIC));
+ }
+
private static float measureText(Typeface typeface, String text) {
Paint paint = new Paint();
paint.setTypeface(typeface);
diff --git a/core/tests/coretests/src/android/util/CharsetUtilsTest.java b/core/tests/coretests/src/android/util/CharsetUtilsTest.java
index 04cb3d7..c295451 100644
--- a/core/tests/coretests/src/android/util/CharsetUtilsTest.java
+++ b/core/tests/coretests/src/android/util/CharsetUtilsTest.java
@@ -40,37 +40,47 @@
}
@Test
- public void testUtf8_Empty() {
- assertEquals(0, CharsetUtils.toUtf8Bytes("", destPtr, 0, dest.length));
+ public void testModifiedUtf8_Empty() {
+ assertEquals(0, CharsetUtils.toModifiedUtf8Bytes("", destPtr, 0, dest.length));
assertEquals("0000000000000000", HexDump.toHexString(dest));
+ assertEquals("", CharsetUtils.fromModifiedUtf8Bytes(destPtr, 0, 0));
}
@Test
- public void testUtf8_Simple() {
- assertEquals(7, CharsetUtils.toUtf8Bytes("example", destPtr, 0, dest.length));
+ public void testModifiedUtf8_Null() {
+ assertEquals(4, CharsetUtils.toModifiedUtf8Bytes("!\0!", destPtr, 0, dest.length));
+ assertEquals("21C0802100000000", HexDump.toHexString(dest));
+ assertEquals("!\0!", CharsetUtils.fromModifiedUtf8Bytes(destPtr, 0, 4));
+ }
+
+ @Test
+ public void testModifiedUtf8_Simple() {
+ assertEquals(7, CharsetUtils.toModifiedUtf8Bytes("example", destPtr, 0, dest.length));
assertEquals("6578616D706C6500", HexDump.toHexString(dest));
+ assertEquals("example", CharsetUtils.fromModifiedUtf8Bytes(destPtr, 0, 7));
}
@Test
- public void testUtf8_Complex() {
- assertEquals(3, CharsetUtils.toUtf8Bytes("☃", destPtr, 4, dest.length));
+ public void testModifiedUtf8_Complex() {
+ assertEquals(3, CharsetUtils.toModifiedUtf8Bytes("☃", destPtr, 4, dest.length));
assertEquals("00000000E2988300", HexDump.toHexString(dest));
+ assertEquals("☃", CharsetUtils.fromModifiedUtf8Bytes(destPtr, 4, 3));
}
@Test
- public void testUtf8_Bounds() {
- assertEquals(-1, CharsetUtils.toUtf8Bytes("foo", destPtr, 0, 0));
- assertEquals(-1, CharsetUtils.toUtf8Bytes("foo", destPtr, 0, 2));
- assertEquals(-1, CharsetUtils.toUtf8Bytes("foo", destPtr, -2, 8));
- assertEquals(-1, CharsetUtils.toUtf8Bytes("foo", destPtr, 6, 8));
- assertEquals(-1, CharsetUtils.toUtf8Bytes("foo", destPtr, 10, 8));
+ public void testModifiedUtf8_Bounds() {
+ assertEquals(-3, CharsetUtils.toModifiedUtf8Bytes("foo", destPtr, 0, 0));
+ assertEquals(-3, CharsetUtils.toModifiedUtf8Bytes("foo", destPtr, 0, 2));
+ assertEquals(-3, CharsetUtils.toModifiedUtf8Bytes("foo", destPtr, -2, 8));
+ assertEquals(-3, CharsetUtils.toModifiedUtf8Bytes("foo", destPtr, 6, 8));
+ assertEquals(-3, CharsetUtils.toModifiedUtf8Bytes("foo", destPtr, 10, 8));
}
@Test
- public void testUtf8_Overwrite() {
- assertEquals(5, CharsetUtils.toUtf8Bytes("!!!!!", destPtr, 0, dest.length));
- assertEquals(3, CharsetUtils.toUtf8Bytes("...", destPtr, 0, dest.length));
- assertEquals(1, CharsetUtils.toUtf8Bytes("?", destPtr, 0, dest.length));
+ public void testModifiedUtf8_Overwrite() {
+ assertEquals(5, CharsetUtils.toModifiedUtf8Bytes("!!!!!", destPtr, 0, dest.length));
+ assertEquals(3, CharsetUtils.toModifiedUtf8Bytes("...", destPtr, 0, dest.length));
+ assertEquals(1, CharsetUtils.toModifiedUtf8Bytes("?", destPtr, 0, dest.length));
assertEquals("3F002E0021000000", HexDump.toHexString(dest));
}
}
diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java
index 2ae9cdf..a30381a 100644
--- a/core/tests/coretests/src/android/util/XmlTest.java
+++ b/core/tests/coretests/src/android/util/XmlTest.java
@@ -64,7 +64,7 @@
*/
private static void doLargeValues(TypedXmlSerializer out, TypedXmlPullParser in)
throws Exception {
- final char[] chars = new char[(1 << 16) - 1];
+ final char[] chars = new char[65_534];
Arrays.fill(chars, '!');
final String string = new String(chars);
diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
index 8efd3b4..7b9283b 100644
--- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
@@ -16,12 +16,11 @@
package android.widget;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
-import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT;
-import static android.widget.TextViewOnReceiveContentCallback.canReuse;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD;
+import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static androidx.test.espresso.Espresso.onView;
@@ -42,8 +41,7 @@
import android.content.ClipDescription;
import android.net.Uri;
import android.os.Bundle;
-import android.util.ArraySet;
-import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentListener;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputContentInfo;
@@ -62,7 +60,7 @@
import org.mockito.Mockito;
/**
- * Tests for {@link TextViewOnReceiveContentCallback}. Most of the test cases are in the CTS test
+ * Tests for {@link TextViewOnReceiveContentListener}. Most of the test cases are in the CTS test
* {@link android.widget.cts.TextViewOnReceiveContentTest}. This class tests some internal
* implementation details, e.g. fallback to the keyboard image API.
*/
@@ -78,35 +76,34 @@
private Instrumentation mInstrumentation;
private Activity mActivity;
private CustomInputConnectionEditText mEditText;
- private TextViewOnReceiveContentCallback mDefaultCallback;
+ private TextViewOnReceiveContentListener mDefaultReceiver;
@Before
public void before() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
mEditText = mActivity.findViewById(R.id.edittext2);
- mDefaultCallback = mEditText.getEditorForTesting().getDefaultOnReceiveContentCallback();
+ mDefaultReceiver = mEditText.getEditorForTesting().getDefaultOnReceiveContentListener();
}
@Test
- public void testGetSupportedMimeTypes_fallbackToCommitContent() throws Throwable {
+ public void testGetEditorInfoMimeTypes_fallbackToCommitContent() throws Throwable {
// Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
// types.
- mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+ String[] mimeTypes = {"image/gif", "image/png"};
+ mEditText.setContentMimeTypes(mimeTypes);
MyInputConnection ic = new MyInputConnection();
mEditText.setInputConnectionWrapper(ic);
// Focus into the EditText.
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
- // Assert that the callback returns the MIME types declared in the EditorInfo in addition to
- // the default.
- assertThat(mDefaultCallback.getMimeTypes(mEditText)).containsExactly(
- "text/*", "image/gif", "image/png");
+ // Assert that the default listener returns the MIME types declared in the EditorInfo.
+ assertThat(mDefaultReceiver.getEditorInfoMimeTypes(mEditText)).isEqualTo(mimeTypes);
}
@Test
- public void testGetSupportedMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo()
+ public void testGetEditorInfoMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo()
throws Throwable {
// Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME
// types.
@@ -117,8 +114,8 @@
// Focus into the EditText.
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
- // Assert that the callback returns the default MIME types.
- assertThat(mDefaultCallback.getMimeTypes(mEditText)).containsExactly("text/*");
+ // Assert that the default listener returns null as the MIME types.
+ assertThat(mDefaultReceiver.getEditorInfoMimeTypes(mEditText)).isNull();
}
@Test
@@ -132,13 +129,13 @@
// Focus into the EditText.
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
- // Invoke the callback with SOURCE_AUTOFILL and assert that it triggers a call to
+ // Invoke the listener with SOURCE_AUTOFILL and assert that it triggers a call to
// InputConnection.commitContent.
ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
- OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
- mDefaultCallback.onReceiveContent(mEditText, payload);
+ OnReceiveContentListener.Payload payload =
+ new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+ mDefaultReceiver.onReceiveContent(mEditText, payload);
verify(ic.mMock, times(1))
.commitContent(any(InputContentInfo.class), eq(0), eq(null));
verifyNoMoreInteractions(ic.mMock);
@@ -155,12 +152,12 @@
// Focus into the EditText.
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
- // Invoke the callback and assert that the InputConnection is not invoked.
+ // Invoke the listener and assert that the InputConnection is not invoked.
ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
- OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
- mDefaultCallback.onReceiveContent(mEditText, payload);
+ OnReceiveContentListener.Payload payload =
+ new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+ mDefaultReceiver.onReceiveContent(mEditText, payload);
verifyZeroInteractions(ic.mMock);
}
@@ -175,71 +172,28 @@
// Focus into the EditText.
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
- // Invoke the callback with sources other than SOURCE_AUTOFILL and assert that it does NOT
+ // Invoke the listener with sources other than SOURCE_AUTOFILL and assert that it does NOT
// trigger calls to InputConnection.commitContent.
ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
- OnReceiveContentCallback.Payload payload =
- new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD).build();
- mDefaultCallback.onReceiveContent(mEditText, payload);
+ OnReceiveContentListener.Payload payload =
+ new OnReceiveContentListener.Payload.Builder(clip, SOURCE_CLIPBOARD).build();
+ mDefaultReceiver.onReceiveContent(mEditText, payload);
verifyZeroInteractions(ic.mMock);
- payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD).build();
- mDefaultCallback.onReceiveContent(mEditText, payload);
+ payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD).build();
+ mDefaultReceiver.onReceiveContent(mEditText, payload);
verifyZeroInteractions(ic.mMock);
- payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build();
- mDefaultCallback.onReceiveContent(mEditText, payload);
+ payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build();
+ mDefaultReceiver.onReceiveContent(mEditText, payload);
verifyZeroInteractions(ic.mMock);
- payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
- mDefaultCallback.onReceiveContent(mEditText, payload);
+ payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
+ mDefaultReceiver.onReceiveContent(mEditText, payload);
verifyZeroInteractions(ic.mMock);
}
- @Test
- public void testCanReuse() throws Throwable {
- ArraySet<String> mimeTypes = null;
- String[] editorContentMimeTypes = new String[0];
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
-
- mimeTypes = new ArraySet<>();
- editorContentMimeTypes = new String[0];
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
-
- mimeTypes = newArraySet("text/*");
- editorContentMimeTypes = new String[0];
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
-
- mimeTypes = newArraySet("text/*");
- editorContentMimeTypes = new String[] {"text/*"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
-
- mimeTypes = newArraySet("image/gif", "image/png", "text/*");
- editorContentMimeTypes = new String[] {"image/gif", "image/png"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
-
- mimeTypes = newArraySet("image/gif", "image/png", "text/*");
- editorContentMimeTypes = new String[] {"image/gif", "image/png", "text/*"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
-
- mimeTypes = newArraySet("image/gif", "image/png", "text/*");
- editorContentMimeTypes = new String[] {"image/gif"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
-
- mimeTypes = newArraySet("image/gif", "image/png", "text/*");
- editorContentMimeTypes = new String[] {"image/gif", "image/png", "image/jpg"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
-
- mimeTypes = newArraySet("image/gif", "image/png", "text/*");
- editorContentMimeTypes = new String[] {"image/gif", "image/jpg"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
-
- mimeTypes = newArraySet("image/gif", "image/png", "text/*");
- editorContentMimeTypes = new String[] {"image/gif", "image/jpg", "text/*"};
- assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
- }
-
private static class MyInputConnection extends InputConnectionWrapper {
public final InputConnection mMock;
@@ -254,9 +208,4 @@
return true;
}
}
-
- @SafeVarargs
- private static <T> ArraySet<T> newArraySet(T ... elements) {
- return new ArraySet<>(elements);
- }
}
diff --git a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
index 841d659..81fb39f 100644
--- a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
@@ -30,6 +30,10 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -83,7 +87,7 @@
@Test
public void testUTF_Bounds() throws Exception {
- final char[] buf = new char[65_535];
+ final char[] buf = new char[65_534];
try (FastDataOutput out = new FastDataOutput(new ByteArrayOutputStream(), BOUNCE_SIZE)) {
// Writing simple string will fit fine
Arrays.fill(buf, '!');
@@ -100,6 +104,61 @@
}
@Test
+ public void testTranscode() throws Exception {
+ // Verify that upstream data can be read by fast
+ {
+ final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ final DataOutputStream out = new DataOutputStream(outStream);
+ doTranscodeWrite(out);
+ out.flush();
+
+ final FastDataInput in = new FastDataInput(
+ new ByteArrayInputStream(outStream.toByteArray()), BOUNCE_SIZE);
+ doTransodeRead(in);
+ }
+
+ // Verify that fast data can be read by upstream
+ {
+ final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ final FastDataOutput out = new FastDataOutput(outStream, BOUNCE_SIZE);
+ doTranscodeWrite(out);
+ out.flush();
+
+ final DataInputStream in = new DataInputStream(
+ new ByteArrayInputStream(outStream.toByteArray()));
+ doTransodeRead(in);
+ }
+ }
+
+ private static void doTranscodeWrite(DataOutput out) throws IOException {
+ out.writeBoolean(true);
+ out.writeBoolean(false);
+ out.writeByte(1);
+ out.writeShort(2);
+ out.writeInt(4);
+ out.writeUTF("foo\0bar");
+ out.writeUTF(TEST_SHORT_STRING);
+ out.writeUTF(TEST_LONG_STRING);
+ out.writeLong(8L);
+ out.writeFloat(16f);
+ out.writeDouble(32d);
+ }
+
+ private static void doTransodeRead(DataInput in) throws IOException {
+ assertEquals(true, in.readBoolean());
+ assertEquals(false, in.readBoolean());
+ assertEquals(1, in.readByte());
+ assertEquals(2, in.readShort());
+ assertEquals(4, in.readInt());
+ assertEquals("foo\0bar", in.readUTF());
+ assertEquals(TEST_SHORT_STRING, in.readUTF());
+ assertEquals(TEST_LONG_STRING, in.readUTF());
+ assertEquals(8L, in.readLong());
+ assertEquals(16f, in.readFloat(), 0.01);
+ assertEquals(32d, in.readDouble(), 0.01);
+ }
+
+ @Test
public void testBounce_Char() throws Exception {
doBounce((out) -> {
out.writeChar('\0');
@@ -191,7 +250,7 @@
@Test
public void testBounce_UTF_Maximum() throws Exception {
- final char[] expectedBuf = new char[65_535];
+ final char[] expectedBuf = new char[65_534];
Arrays.fill(expectedBuf, '!');
final String expected = new String(expectedBuf);
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
index 37f64b5..474cd54 100644
--- a/data/etc/car/com.android.car.provision.xml
+++ b/data/etc/car/com.android.car.provision.xml
@@ -18,6 +18,7 @@
<privapp-permissions package="com.android.car.provision">
<permission name="android.car.permission.CAR_POWERTRAIN"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MASTER_CLEAR"/>
<permission name="android.permission.QUERY_ALL_PACKAGES"/>
<permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 42ebfc5..8406fdf 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -356,6 +356,8 @@
<!-- Needed for test only -->
<permission name="android.permission.READ_PRECISE_PHONE_STATE" />
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <!-- Permission required for UiModeManager CTS test -->
+ <permission name="android.permission.READ_PROJECTION_STATE"/>
<permission name="android.permission.READ_WIFI_CREDENTIAL"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -376,7 +378,7 @@
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.SUSPEND_APPS" />
- <!-- Permissions required for UiModeManager and Telecom car mode CTS tests -->
+ <!-- Permission required for UiModeManager and Telecom car mode CTS tests -->
<permission name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION" />
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java
new file mode 100644
index 0000000..b5f26e7
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.anyOf;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+import static com.google.errorprone.matchers.Matchers.stringLiteral;
+
+import com.google.auto.service.AutoService;
+import com.google.common.base.Objects;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePathScanner;
+import com.sun.tools.javac.code.Type;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.lang.model.element.Name;
+
+/**
+ * Android offers {@code TypedXmlSerializer} and {@code TypedXmlPullParser} to
+ * more efficiently store primitive values.
+ * <p>
+ * This checker identifies callers that are manually converting strings instead
+ * of relying on efficient strongly-typed methods.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkEfficientXml",
+ summary = "Verifies efficient XML best-practices",
+ severity = WARNING)
+public final class EfficientXmlChecker extends BugChecker
+ implements MethodInvocationTreeMatcher, NewClassTreeMatcher {
+ private static final String STRING = "java.lang.String";
+ private static final String INTEGER = "java.lang.Integer";
+ private static final String LONG = "java.lang.Long";
+ private static final String FLOAT = "java.lang.Float";
+ private static final String DOUBLE = "java.lang.Double";
+ private static final String BOOLEAN = "java.lang.Boolean";
+
+ private static final Matcher<ExpressionTree> BOOLEAN_STRING_LITERAL = stringLiteral(
+ Pattern.compile("(true|false)"));
+
+ private static final Matcher<ExpressionTree> PRIMITIVE_TO_STRING = anyOf(
+ methodInvocation(staticMethod().onClass(INTEGER).named("toString")),
+ methodInvocation(staticMethod().onClass(INTEGER).named("toHexString")),
+ methodInvocation(staticMethod().onClass(LONG).named("toString")),
+ methodInvocation(staticMethod().onClass(LONG).named("toHexString")),
+ methodInvocation(staticMethod().onClass(FLOAT).named("toString")),
+ methodInvocation(staticMethod().onClass(DOUBLE).named("toString")),
+ methodInvocation(staticMethod().onClass(BOOLEAN).named("toString")),
+ methodInvocation(instanceMethod().onExactClass(INTEGER).named("toString")),
+ methodInvocation(instanceMethod().onExactClass(LONG).named("toString")),
+ methodInvocation(instanceMethod().onExactClass(FLOAT).named("toString")),
+ methodInvocation(instanceMethod().onExactClass(DOUBLE).named("toString")),
+ methodInvocation(instanceMethod().onExactClass(BOOLEAN).named("toString")));
+
+ private static final Matcher<ExpressionTree> VALUE_OF_PRIMITIVE = anyOf(
+ methodInvocation(staticMethod().onClass(STRING).withSignature("valueOf(int)")),
+ methodInvocation(staticMethod().onClass(STRING).withSignature("valueOf(long)")),
+ methodInvocation(staticMethod().onClass(STRING).withSignature("valueOf(float)")),
+ methodInvocation(staticMethod().onClass(STRING).withSignature("valueOf(double)")),
+ methodInvocation(staticMethod().onClass(STRING).withSignature("valueOf(boolean)")));
+
+ private static final Matcher<ExpressionTree> VALUE_OF_OBJECT = methodInvocation(
+ staticMethod().onClass(STRING).withSignature("valueOf(java.lang.Object)"));
+
+ private static final Matcher<ExpressionTree> PRIMITIVE_PARSE = anyOf(
+ methodInvocation(staticMethod().onClass(INTEGER).named("parseInt")),
+ methodInvocation(staticMethod().onClass(INTEGER)
+ .withSignature("valueOf(java.lang.String)")),
+ methodInvocation(staticMethod().onClass(INTEGER)
+ .withSignature("valueOf(java.lang.String,int)")),
+ methodInvocation(staticMethod().onClass(LONG).named("parseLong")),
+ methodInvocation(staticMethod().onClass(LONG)
+ .withSignature("valueOf(java.lang.String)")),
+ methodInvocation(staticMethod().onClass(LONG)
+ .withSignature("valueOf(java.lang.String,int)")),
+ methodInvocation(staticMethod().onClass(FLOAT).named("parseFloat")),
+ methodInvocation(staticMethod().onClass(FLOAT)
+ .withSignature("valueOf(java.lang.String)")),
+ methodInvocation(staticMethod().onClass(DOUBLE).named("parseDouble")),
+ methodInvocation(staticMethod().onClass(DOUBLE)
+ .withSignature("valueOf(java.lang.String)")),
+ methodInvocation(staticMethod().onClass(BOOLEAN).named("parseBoolean")),
+ methodInvocation(staticMethod().onClass(BOOLEAN)
+ .withSignature("valueOf(java.lang.String)")));
+
+ private static final Matcher<Tree> IS_FAST_XML_SERIALIZER =
+ isSubtypeOf("com.android.internal.util.FastXmlSerializer");
+
+ private static final Matcher<ExpressionTree> WRITE_ATTRIBUTE = methodInvocation(
+ instanceMethod().onDescendantOf("org.xmlpull.v1.XmlSerializer")
+ .named("attribute"));
+
+ private static final Matcher<ExpressionTree> READ_ATTRIBUTE = methodInvocation(
+ instanceMethod().onDescendantOf("org.xmlpull.v1.XmlPullParser")
+ .named("getAttributeValue"));
+
+ private static final Matcher<ExpressionTree> XML_FACTORY = methodInvocation(staticMethod()
+ .onClass("android.util.Xml").namedAnyOf("newSerializer", "newPullParser"));
+
+ private static final Matcher<ExpressionTree> BYTES_TO_STRING = anyOf(
+ methodInvocation(staticMethod().onClass("android.util.Base64")
+ .named("encodeToString")),
+ methodInvocation(instanceMethod().onDescendantOf("java.util.Base64.Encoder")
+ .named("encodeToString")),
+ methodInvocation(staticMethod().onClass("libcore.util.HexEncoding")
+ .named("encodeToString")),
+ methodInvocation(staticMethod().onClass("com.android.internal.util.HexDump")
+ .named("toHexString")));
+
+ private static final Matcher<ExpressionTree> BYTES_FROM_STRING = anyOf(
+ methodInvocation(staticMethod().onClass("android.util.Base64")
+ .named("decode")),
+ methodInvocation(instanceMethod().onDescendantOf("java.util.Base64.Decoder")
+ .named("decode")),
+ methodInvocation(staticMethod().onClass("libcore.util.HexEncoding")
+ .named("decode")),
+ methodInvocation(staticMethod().onClass("com.android.internal.util.HexDump")
+ .named("hexStringToByteArray")));
+
+ private static final String MESSAGE_WRITE = "Primitive values can be written more "
+ + "efficiently by using TypedXmlSerializer overloads";
+ private static final String MESSAGE_READ = "Primitive values can be parsed more "
+ + "efficiently by using TypedXmlPullParser overloads";
+ private static final String MESSAGE_CTOR = "Primitive values can be parsed more "
+ + "efficiently by using Xml.resolveSerializer() and/or Xml.resolvePullParser()";
+
+ /**
+ * Determine if the given expression is attempting to convert a primitive to
+ * a {@link String}.
+ */
+ private static final Matcher<ExpressionTree> CONVERT_PRIMITIVE_TO_STRING =
+ new Matcher<ExpressionTree>() {
+ @Override
+ public boolean matches(ExpressionTree tree, VisitorState state) {
+ if (PRIMITIVE_TO_STRING.matches(tree, state)) {
+ final List<? extends ExpressionTree> args = ((MethodInvocationTree) tree)
+ .getArguments();
+ // We're only interested in base-10 or base-16 numerical conversions
+ if (args.size() <= 1 || String.valueOf(args.get(1)).equals("10")
+ || String.valueOf(args.get(1)).equals("16")) {
+ return true;
+ }
+ } else if (VALUE_OF_PRIMITIVE.matches(tree, state)) {
+ return true;
+ } else if (VALUE_OF_OBJECT.matches(tree, state)) {
+ final Type type = ASTHelpers.getResultType(((MethodInvocationTree) tree)
+ .getArguments().get(0));
+ if (ASTHelpers.isSameType(type, state.getTypeFromString(INTEGER), state)
+ || ASTHelpers.isSameType(type, state.getTypeFromString(LONG), state)
+ || ASTHelpers.isSameType(type, state.getTypeFromString(FLOAT), state)
+ || ASTHelpers.isSameType(type, state.getTypeFromString(DOUBLE), state)
+ || ASTHelpers.isSameType(type, state.getTypeFromString(BOOLEAN), state)) {
+ return true;
+ }
+ } else if (BOOLEAN_STRING_LITERAL.matches(tree, state)) {
+ return true;
+ } else if (BYTES_TO_STRING.matches(tree, state)) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ /**
+ * Determine if the given expression is attempting to convert a
+ * {@link String} to a primitive.
+ */
+ private static final Matcher<ExpressionTree> CONVERT_STRING_TO_PRIMITIVE =
+ new Matcher<ExpressionTree>() {
+ @Override
+ public boolean matches(ExpressionTree tree, VisitorState state) {
+ if (PRIMITIVE_PARSE.matches(tree, state)) {
+ final List<? extends ExpressionTree> args = ((MethodInvocationTree) tree)
+ .getArguments();
+ // We're only interested in base-10 or base-16 numerical conversions
+ if (args.size() <= 1 || String.valueOf(args.get(1)).equals("10")
+ || String.valueOf(args.get(1)).equals("16")) {
+ return true;
+ }
+ } else if (BYTES_FROM_STRING.matches(tree, state)) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (WRITE_ATTRIBUTE.matches(tree, state)) {
+ final List<? extends ExpressionTree> args = tree.getArguments();
+ final ExpressionTree writeSource = args.get(2);
+ if (CONVERT_PRIMITIVE_TO_STRING.matches(writeSource, state)) {
+ return buildDescription(tree).setMessage(MESSAGE_WRITE).build();
+ }
+
+ // Hunt around for related conversions
+ if (writeSource instanceof IdentifierTree) {
+ final Name name = ((IdentifierTree) writeSource).getName();
+ final Description res = new TreePathScanner<Description, Void>() {
+ @Override
+ public Description reduce(Description r1, Description r2) {
+ return (r1 != null) ? r1 : r2;
+ }
+
+ @Override
+ public Description visitVariable(VariableTree node, Void param) {
+ return visitWriteSource(node.getName(), node.getInitializer());
+ }
+
+ @Override
+ public Description visitAssignment(AssignmentTree node, Void param) {
+ final ExpressionTree variable = node.getVariable();
+ if (variable instanceof IdentifierTree) {
+ return visitWriteSource(((IdentifierTree) variable).getName(),
+ node.getExpression());
+ } else {
+ return super.visitAssignment(node, param);
+ }
+ }
+
+ private Description visitWriteSource(Name target, ExpressionTree source) {
+ if (CONVERT_PRIMITIVE_TO_STRING.matches(source, state)
+ && Objects.equal(name, target)) {
+ return buildDescription(source).setMessage(MESSAGE_WRITE).build();
+ } else {
+ return null;
+ }
+ }
+ }.scan(state.findPathToEnclosing(MethodTree.class), null);
+ if (res != null) {
+ return res;
+ }
+ }
+ } else if (READ_ATTRIBUTE.matches(tree, state)) {
+ final Tree readDest = state.getPath().getParentPath().getLeaf();
+ if (readDest instanceof ExpressionTree
+ && CONVERT_STRING_TO_PRIMITIVE.matches((ExpressionTree) readDest, state)) {
+ return buildDescription(tree).setMessage(MESSAGE_READ).build();
+ }
+
+ // Hunt around for related conversions
+ Name name = null;
+ if (readDest instanceof VariableTree) {
+ name = ((VariableTree) readDest).getName();
+ } else if (readDest instanceof AssignmentTree) {
+ final ExpressionTree variable = ((AssignmentTree) readDest).getVariable();
+ if (variable instanceof IdentifierTree) {
+ name = ((IdentifierTree) variable).getName();
+ }
+ }
+ if (name != null) {
+ final Name fName = name;
+ final Description res = new TreePathScanner<Description, Void>() {
+ @Override
+ public Description reduce(Description r1, Description r2) {
+ return (r1 != null) ? r1 : r2;
+ }
+
+ @Override
+ public Description visitMethodInvocation(MethodInvocationTree node,
+ Void param) {
+ if (CONVERT_STRING_TO_PRIMITIVE.matches(node, state)) {
+ final ExpressionTree arg = node.getArguments().get(0);
+ if (arg instanceof IdentifierTree
+ && Objects.equal(((IdentifierTree) arg).getName(), fName)) {
+ return buildDescription(node).setMessage(MESSAGE_READ).build();
+ }
+ }
+ return super.visitMethodInvocation(node, param);
+ }
+ }.scan(state.findPathToEnclosing(MethodTree.class), null);
+ if (res != null) {
+ return res;
+ }
+ }
+ } else if (XML_FACTORY.matches(tree, state)) {
+ return buildDescription(tree).setMessage(MESSAGE_CTOR).build();
+ }
+ return Description.NO_MATCH;
+ }
+
+ @Override
+ public Description matchNewClass(NewClassTree tree, VisitorState state) {
+ if (IS_FAST_XML_SERIALIZER.matches(tree, state)) {
+ return buildDescription(tree).setMessage(MESSAGE_CTOR).build();
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientXmlCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientXmlCheckerTest.java
new file mode 100644
index 0000000..084fb25
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientXmlCheckerTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientXmlCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ EfficientXmlChecker.class, getClass());
+ }
+
+ @Test
+ public void testCtor() {
+ compilationHelper
+ .addSourceFile("/android/util/Xml.java")
+ .addSourceFile("/com/android/internal/util/FastXmlSerializer.java")
+ .addSourceLines("Example.java",
+ "import android.util.Xml;",
+ "import com.android.internal.util.FastXmlSerializer;",
+ "public class Example {",
+ " public void writer() throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " Xml.newSerializer();",
+ " // BUG: Diagnostic contains:",
+ " new FastXmlSerializer();",
+ " }",
+ " public void reader() throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " Xml.newPullParser();",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testWrite() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import org.xmlpull.v1.XmlSerializer;",
+ "public class Example {",
+ " public void typical(XmlSerializer out) throws Exception {",
+ " out.attribute(null, null, null);",
+ " out.attribute(null, null, \"foo\");",
+ " out.attribute(null, null, String.valueOf(null));",
+ " out.attribute(null, null, String.valueOf(\"foo\"));",
+ " }",
+ " public void rawBoolean(XmlSerializer out) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, \"true\");",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, \"false\");",
+ " }",
+ " public void toString(XmlSerializer out) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Integer.toString(42));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Integer.toString(42, 10));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Integer.toString(42, 16));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Integer.toHexString(42));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Long.toString(42L));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Long.toString(42L, 10));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Long.toString(42L, 16));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Long.toHexString(42L));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Float.toString(42f));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Double.toString(42d));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Boolean.toString(true));",
+ " }",
+ " public void toStringBoxed(XmlSerializer out) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Integer.valueOf(42).toString());",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Long.valueOf(42L).toString());",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Float.valueOf(42f).toString());",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Double.valueOf(42d).toString());",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Boolean.valueOf(true).toString());",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, Boolean.TRUE.toString());",
+ " }",
+ " public void valueOf(XmlSerializer out) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(42));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(42L));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(42f));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(42d));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(true));",
+ " }",
+ " public void valueOfBoxed(XmlSerializer out) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(Integer.valueOf(42)));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(Long.valueOf(42L)));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(Float.valueOf(42f)));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(Double.valueOf(42d)));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(Boolean.valueOf(true)));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null, String.valueOf(Boolean.TRUE));",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testWrite_Indirect() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import org.xmlpull.v1.XmlSerializer;",
+ "public class Example {",
+ " XmlSerializer out;",
+ " public void argUnknown(String arg) throws Exception {",
+ " out.attribute(null, null, arg);",
+ " }",
+ " public void argNull(String arg) throws Exception {",
+ " arg = null;",
+ " out.attribute(null, null, arg);",
+ " }",
+ " public void argValueOfNull(String arg) throws Exception {",
+ " arg = String.valueOf(null);",
+ " out.attribute(null, null, arg);",
+ " }",
+ " public void argToString(String arg) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " arg = Integer.toString(42);",
+ " out.attribute(null, null, arg);",
+ " }",
+ " public void argValueOf(String arg) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " arg = String.valueOf(42);",
+ " out.attribute(null, null, arg);",
+ " }",
+ " public void directToString() throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " String arg = Integer.toString(42);",
+ " out.attribute(null, null, arg);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testWrite_Bytes() {
+ compilationHelper
+ .addSourceFile("/android/util/Base64.java")
+ .addSourceFile("/libcore/util/HexEncoding.java")
+ .addSourceFile("/com/android/internal/util/HexDump.java")
+ .addSourceLines("Example.java",
+ "import org.xmlpull.v1.XmlSerializer;",
+ "public class Example {",
+ " XmlSerializer out;",
+ " public void bytes(byte[] arg) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null,",
+ " android.util.Base64.encodeToString(arg, 0));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null,",
+ " java.util.Base64.getEncoder().encodeToString(arg));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null,",
+ " libcore.util.HexEncoding.encodeToString(arg));",
+ " // BUG: Diagnostic contains:",
+ " out.attribute(null, null,",
+ " com.android.internal.util.HexDump.toHexString(arg));",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testRead() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import org.xmlpull.v1.XmlPullParser;",
+ "public class Example {",
+ " public void typical(XmlPullParser in) throws Exception {",
+ " in.getAttributeValue(null, null);",
+ " }",
+ " public void parse(XmlPullParser in) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " Integer.parseInt(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Integer.parseInt(in.getAttributeValue(null, null), 10);",
+ " // BUG: Diagnostic contains:",
+ " Integer.parseInt(in.getAttributeValue(null, null), 16);",
+ " // BUG: Diagnostic contains:",
+ " Long.parseLong(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Long.parseLong(in.getAttributeValue(null, null), 10);",
+ " // BUG: Diagnostic contains:",
+ " Long.parseLong(in.getAttributeValue(null, null), 16);",
+ " // BUG: Diagnostic contains:",
+ " Float.parseFloat(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Double.parseDouble(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Boolean.parseBoolean(in.getAttributeValue(null, null));",
+ " }",
+ " public void valueOf(XmlPullParser in) throws Exception {",
+ " // BUG: Diagnostic contains:",
+ " Integer.valueOf(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Long.valueOf(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Float.valueOf(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Double.valueOf(in.getAttributeValue(null, null));",
+ " // BUG: Diagnostic contains:",
+ " Boolean.valueOf(in.getAttributeValue(null, null));",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testRead_Indirect() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import org.xmlpull.v1.XmlPullParser;",
+ "public class Example {",
+ " public int direct(XmlPullParser in) throws Exception {",
+ " String arg = in.getAttributeValue(null, null);",
+ " if (arg != null) {",
+ " // BUG: Diagnostic contains:",
+ " return Integer.parseInt(arg);",
+ " } else {",
+ " return -1;",
+ " }",
+ " }",
+ " public int indirect(XmlPullParser in, String arg) throws Exception {",
+ " arg = in.getAttributeValue(null, null);",
+ " if (arg != null) {",
+ " // BUG: Diagnostic contains:",
+ " return Integer.parseInt(arg);",
+ " } else {",
+ " return -1;",
+ " }",
+ " }",
+ " public int tryCatch(XmlPullParser in) throws Exception {",
+ " String arg = in.getAttributeValue(null, null);",
+ " try {",
+ " // BUG: Diagnostic contains:",
+ " return Integer.parseInt(arg);",
+ " } catch (NumberFormatException e) {",
+ " return -1;",
+ " }",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testRead_Bytes() {
+ compilationHelper
+ .addSourceFile("/android/util/Base64.java")
+ .addSourceFile("/libcore/util/HexEncoding.java")
+ .addSourceFile("/com/android/internal/util/HexDump.java")
+ .addSourceLines("Example.java",
+ "import org.xmlpull.v1.XmlPullParser;",
+ "public class Example {",
+ " XmlPullParser in;",
+ " public void bytes() throws Exception {",
+ " android.util.Base64.decode(",
+ " // BUG: Diagnostic contains:",
+ " in.getAttributeValue(null, null), 0);",
+ " java.util.Base64.getDecoder().decode(",
+ " // BUG: Diagnostic contains:",
+ " in.getAttributeValue(null, null));",
+ " libcore.util.HexEncoding.decode(",
+ " // BUG: Diagnostic contains:",
+ " in.getAttributeValue(null, null));",
+ " com.android.internal.util.HexDump.hexStringToByteArray(",
+ " // BUG: Diagnostic contains:",
+ " in.getAttributeValue(null, null));",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/res/android/util/Base64.java b/errorprone/tests/res/android/util/Base64.java
new file mode 100644
index 0000000..63d80bef
--- /dev/null
+++ b/errorprone/tests/res/android/util/Base64.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+public class Base64 {
+ public static byte[] decode(String str, int flags) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static String encodeToString(byte[] input, int flags) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/android/util/Xml.java b/errorprone/tests/res/android/util/Xml.java
new file mode 100644
index 0000000..0e44d00
--- /dev/null
+++ b/errorprone/tests/res/android/util/Xml.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+public class Xml {
+ public static XmlPullParser newPullParser() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static XmlSerializer newSerializer() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/com/android/internal/util/FastXmlSerializer.java b/errorprone/tests/res/com/android/internal/util/FastXmlSerializer.java
new file mode 100644
index 0000000..83d14a9
--- /dev/null
+++ b/errorprone/tests/res/com/android/internal/util/FastXmlSerializer.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+public class FastXmlSerializer {
+}
diff --git a/errorprone/tests/res/com/android/internal/util/HexDump.java b/errorprone/tests/res/com/android/internal/util/HexDump.java
new file mode 100644
index 0000000..55d3e50
--- /dev/null
+++ b/errorprone/tests/res/com/android/internal/util/HexDump.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+public class HexDump {
+ public static String toHexString(byte[] array) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static byte[] hexStringToByteArray(String hexString) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/libcore/util/HexEncoding.java b/errorprone/tests/res/libcore/util/HexEncoding.java
new file mode 100644
index 0000000..34bbbac
--- /dev/null
+++ b/errorprone/tests/res/libcore/util/HexEncoding.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 libcore.util;
+
+public class HexEncoding {
+ public static String encodeToString(byte[] data) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static byte[] decode(String encoded) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c58e5f3..5743df5 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -34,8 +34,12 @@
import android.graphics.fonts.SystemFonts;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+import android.os.Trace;
import android.provider.FontRequest;
import android.provider.FontsContract;
+import android.system.ErrnoException;
+import android.system.OsConstants;
import android.text.FontConfig;
import android.util.Base64;
import android.util.LongSparseArray;
@@ -50,6 +54,7 @@
import libcore.util.NativeAllocationRegistry;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -57,6 +62,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -79,19 +85,19 @@
Typeface.class.getClassLoader(), nativeGetReleaseFunc());
/** The default NORMAL typeface object */
- public static final Typeface DEFAULT;
+ public static final Typeface DEFAULT = null;
/**
* The default BOLD typeface object. Note: this may be not actually be
* bold, depending on what fonts are installed. Call getStyle() to know
* for sure.
*/
- public static final Typeface DEFAULT_BOLD;
+ public static final Typeface DEFAULT_BOLD = null;
/** The NORMAL style of the default sans serif typeface. */
- public static final Typeface SANS_SERIF;
+ public static final Typeface SANS_SERIF = null;
/** The NORMAL style of the default serif typeface. */
- public static final Typeface SERIF;
+ public static final Typeface SERIF = null;
/** The NORMAL style of the default monospace typeface. */
- public static final Typeface MONOSPACE;
+ public static final Typeface MONOSPACE = null;
/**
* The default {@link Typeface}s for different text styles.
@@ -99,6 +105,7 @@
* It shouldn't be changed for app wide typeface settings. Please use theme and font XML for
* the same purpose.
*/
+ @GuardedBy("SYSTEM_FONT_MAP_LOCK")
@UnsupportedAppUsage(trackingBug = 123769446)
static Typeface[] sDefaults;
@@ -125,16 +132,28 @@
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
private static final Object sDynamicCacheLock = new Object();
+
+ @GuardedBy("SYSTEM_FONT_MAP_LOCK")
static Typeface sDefaultTypeface;
- // Following two fields are not used but left for hiddenapi private list
/**
* sSystemFontMap is read only and unmodifiable.
* Use public API {@link #create(String, int)} to get the typeface for given familyName.
*/
+ @GuardedBy("SYSTEM_FONT_MAP_LOCK")
@UnsupportedAppUsage(trackingBug = 123769347)
- static final Map<String, Typeface> sSystemFontMap;
+ static final Map<String, Typeface> sSystemFontMap = new HashMap<>();
+ // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
+ @GuardedBy("SYSTEM_FONT_MAP_LOCK")
+ static ByteBuffer sSystemFontMapBuffer = null;
+
+ // Lock to guard sSystemFontMap and derived default or public typefaces.
+ // sStyledCacheLock may be held while this lock is held. Holding them in the reverse order may
+ // introduce deadlock.
+ private static final Object SYSTEM_FONT_MAP_LOCK = new Object();
+
+ // This field is used but left for hiddenapi private list
// We cannot support sSystemFallbackMap since we will migrate to public FontFamily API.
/**
* @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
@@ -187,8 +206,16 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static void setDefault(Typeface t) {
- sDefaultTypeface = t;
- nativeSetDefault(t.native_instance);
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ sDefaultTypeface = t;
+ nativeSetDefault(t.native_instance);
+ }
+ }
+
+ private static Typeface getDefault() {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ return sDefaultTypeface;
+ }
}
/** Returns the typeface's weight value */
@@ -833,7 +860,7 @@
style = NORMAL;
}
if (family == null) {
- family = sDefaultTypeface;
+ family = getDefault();
}
// Return early if we're asked for the same face/style
@@ -902,7 +929,7 @@
@IntRange(from = 1, to = 1000) int weight, boolean italic) {
Preconditions.checkArgumentInRange(weight, 0, 1000, "weight");
if (family == null) {
- family = sDefaultTypeface;
+ family = getDefault();
}
return createWeightStyle(family, weight, italic);
}
@@ -944,7 +971,9 @@
* @return the default typeface that corresponds to the style
*/
public static Typeface defaultFromStyle(@Style int style) {
- return sDefaults[style];
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ return sDefaults[style];
+ }
}
/**
@@ -1137,40 +1166,142 @@
}
}
+ /** @hide */
+ public static SharedMemory serializeFontMap(Map<String, Typeface> fontMap)
+ throws IOException, ErrnoException {
+ long[] nativePtrs = new long[fontMap.size()];
+ // The name table will not be large, so let's create a byte array in memory.
+ ByteArrayOutputStream namesBytes = new ByteArrayOutputStream();
+ int i = 0;
+ for (Map.Entry<String, Typeface> entry : fontMap.entrySet()) {
+ nativePtrs[i++] = entry.getValue().native_instance;
+ writeString(namesBytes, entry.getKey());
+ }
+ int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs);
+ // int (typefacesBytesCount), typefaces, namesBytes
+ SharedMemory sharedMemory = SharedMemory.create(
+ "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size());
+ ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN);
+ try {
+ writableBuffer.putInt(typefacesBytesCount);
+ int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs);
+ if (writtenBytesCount != typefacesBytesCount) {
+ throw new IOException(String.format("Unexpected bytes written: %d, expected: %d",
+ writtenBytesCount, typefacesBytesCount));
+ }
+ writableBuffer.position(writableBuffer.position() + writtenBytesCount);
+ writableBuffer.put(namesBytes.toByteArray());
+ } finally {
+ SharedMemory.unmap(writableBuffer);
+ }
+ sharedMemory.setProtect(OsConstants.PROT_READ);
+ return sharedMemory;
+ }
+
+ // buffer's byte order should be BIG_ENDIAN.
+ /** @hide */
+ @VisibleForTesting
+ public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
+ Map<String, Typeface> fontMap = new HashMap<>();
+ int typefacesBytesCount = buffer.getInt();
+ long[] nativePtrs = nativeReadTypefaces(buffer.slice());
+ if (nativePtrs == null) {
+ throw new IOException("Could not read typefaces");
+ }
+ buffer.position(buffer.position() + typefacesBytesCount);
+ for (long nativePtr : nativePtrs) {
+ String name = readString(buffer);
+ fontMap.put(name, new Typeface(nativePtr));
+ }
+ return fontMap;
+ }
+
+ private static String readString(ByteBuffer buffer) {
+ int length = buffer.getInt();
+ byte[] bytes = new byte[length];
+ buffer.get(bytes);
+ return new String(bytes);
+ }
+
+ private static void writeString(ByteArrayOutputStream bos, String value) throws IOException {
+ byte[] bytes = value.getBytes();
+ writeInt(bos, bytes.length);
+ bos.write(bytes);
+ }
+
+ private static void writeInt(ByteArrayOutputStream bos, int value) {
+ // Write in the big endian order.
+ bos.write((value >> 24) & 0xFF);
+ bos.write((value >> 16) & 0xFF);
+ bos.write((value >> 8) & 0xFF);
+ bos.write(value & 0xFF);
+ }
+
+ /**
+ * Deserialize font map and set it as system font map. This method should be called at most once
+ * per process.
+ */
+ /** @hide */
+ public static void setSystemFontMap(SharedMemory sharedMemory)
+ throws IOException, ErrnoException {
+ if (sSystemFontMapBuffer != null) {
+ throw new UnsupportedOperationException(
+ "Once set, buffer-based system font map cannot be updated");
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setSystemFontMap");
+ try {
+ sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
+ Map<String, Typeface> systemFontMap = deserializeFontMap(sSystemFontMapBuffer);
+ setSystemFontMap(systemFontMap);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static void setSystemFontMap(Map<String, Typeface> systemFontMap) {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ sSystemFontMap.clear();
+ sSystemFontMap.putAll(systemFontMap);
+
+ // We can't assume DEFAULT_FAMILY available on Roboletric.
+ if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) {
+ setDefault(sSystemFontMap.get(DEFAULT_FAMILY));
+ }
+
+ // Set up defaults and typefaces exposed in public API
+ // Use sDefaultTypeface here, because create(String, int) uses DEFAULT as fallback.
+ nativeForceSetStaticFinalField("DEFAULT", create(sDefaultTypeface, 0));
+ nativeForceSetStaticFinalField("DEFAULT_BOLD", create(sDefaultTypeface, Typeface.BOLD));
+ nativeForceSetStaticFinalField("SANS_SERIF", create("sans-serif", 0));
+ nativeForceSetStaticFinalField("SERIF", create("serif", 0));
+ nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0));
+
+ sDefaults = new Typeface[]{
+ DEFAULT,
+ DEFAULT_BOLD,
+ create((String) null, Typeface.ITALIC),
+ create((String) null, Typeface.BOLD_ITALIC),
+ };
+
+ // A list of generic families to be registered in native.
+ // https://www.w3.org/TR/css-fonts-4/#generic-font-families
+ String[] genericFamilies = {
+ "serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui"
+ };
+
+ for (String genericFamily : genericFamilies) {
+ registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily));
+ }
+ }
+ }
+
static {
final HashMap<String, Typeface> systemFontMap = new HashMap<>();
initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
SystemFonts.getAliases());
- sSystemFontMap = Collections.unmodifiableMap(systemFontMap);
-
- // We can't assume DEFAULT_FAMILY available on Roboletric.
- if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) {
- setDefault(sSystemFontMap.get(DEFAULT_FAMILY));
- }
-
- // Set up defaults and typefaces exposed in public API
- DEFAULT = create((String) null, 0);
- DEFAULT_BOLD = create((String) null, Typeface.BOLD);
- SANS_SERIF = create("sans-serif", 0);
- SERIF = create("serif", 0);
- MONOSPACE = create("monospace", 0);
-
- sDefaults = new Typeface[] {
- DEFAULT,
- DEFAULT_BOLD,
- create((String) null, Typeface.ITALIC),
- create((String) null, Typeface.BOLD_ITALIC),
- };
-
- // A list of generic families to be registered in native.
- // https://www.w3.org/TR/css-fonts-4/#generic-font-families
- String[] genericFamilies = {
- "serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui"
- };
-
- for (String genericFamily : genericFamilies) {
- registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily));
- }
+ setSystemFontMap(systemFontMap);
}
@Override
@@ -1210,36 +1341,6 @@
return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
}
- /**
- * Writes Typeface instances to the ByteBuffer and returns the number of bytes written.
- *
- * <p>If {@code buffer} is null, this method returns the number of bytes required to serialize
- * the typefaces, without writing anything.
- * @hide
- */
- public static int writeTypefaces(
- @Nullable ByteBuffer buffer, @NonNull List<Typeface> typefaces) {
- long[] nativePtrs = new long[typefaces.size()];
- for (int i = 0; i < nativePtrs.length; i++) {
- nativePtrs[i] = typefaces.get(i).native_instance;
- }
- return nativeWriteTypefaces(buffer, nativePtrs);
- }
-
- /**
- * Reads serialized Typeface instances from the ByteBuffer. Returns null on errors.
- * @hide
- */
- public static @Nullable List<Typeface> readTypefaces(@NonNull ByteBuffer buffer) {
- long[] nativePtrs = nativeReadTypefaces(buffer);
- if (nativePtrs == null) return null;
- List<Typeface> typefaces = new ArrayList<>(nativePtrs.length);
- for (long nativePtr : nativePtrs) {
- typefaces.add(new Typeface(nativePtr));
- }
- return typefaces;
- }
-
private static native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateFromTypefaceWithExactStyle(
long native_instance, int weight, boolean italic);
@@ -1270,4 +1371,6 @@
@Nullable ByteBuffer buffer, @NonNull long[] nativePtrs);
private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
+
+ private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
}
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 6342c00..8213f2f 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -79,6 +79,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
},
+ "-742394458": {
+ "message": "pair task1=%d task2=%d in AppPair=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPair.java"
+ },
"-710770147": {
"message": "Add target: %s",
"level": "VERBOSE",
@@ -91,6 +97,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
},
+ "-234284913": {
+ "message": "unpair taskId=%d pair=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
+ },
"-191422040": {
"message": "Transition animations finished, notifying core %s",
"level": "VERBOSE",
@@ -139,12 +151,30 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "900599280": {
+ "message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
+ "level": "ERROR",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPair.java"
+ },
+ "950299522": {
+ "message": "taskId %d isn't isn't in an app-pair.",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
+ },
"980952660": {
"message": "Task root back pressed taskId=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "1079041527": {
+ "message": "incrementPool size=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
+ },
"1104702476": {
"message": "Letterbox Task Changed: #%d",
"level": "VERBOSE",
@@ -175,12 +205,24 @@
"group": "WM_SHELL_DRAG_AND_DROP",
"at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
},
+ "1891981945": {
+ "message": "release entry.taskId=%s listener=%s size=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
+ },
"1990759023": {
"message": "addListenerForType types=%s listener=%s",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "2006473416": {
+ "message": "acquire entry.taskId=%s listener=%s size=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
+ },
"2057038970": {
"message": "Display changed: %d",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
new file mode 100644
index 0000000..7698e51
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Maak toe"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Vou uit"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Instellings"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Kieslys"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in beeld-in-beeld"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"As jy nie wil hê dat <xliff:g id="NAME">%s</xliff:g> hierdie kenmerk moet gebruik nie, tik om instellings oop te maak en skakel dit af."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Speel"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Laat wag"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Slaan oor na volgende"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Slaan oor na vorige"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Verander grootte"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Program steun nie begin op sekondêre skerms nie."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skermverdeler"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Volskerm links"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Links 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Links 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Links 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Volskerm regs"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Volskerm bo"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Bo 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bo 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bo 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Volskerm onder"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellings vir <xliff:g id="APP_NAME">%1$s</xliff:g>-borrels"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Oorloop"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Voeg terug op stapel"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> vanaf <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g> en <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> meer af"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Beweeg na links bo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Klets met borrels"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nuwe gesprekke verskyn as swerwende ikone, of borrels Tik op borrel om dit oop te maak. Sleep om dit te skuif."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer borrels enige tyd"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Bestuur om borrels vanaf hierdie program af te skakel"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen onlangse borrels nie"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Onlangse borrels en borrels wat toegemaak is, sal hier verskyn"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
new file mode 100644
index 0000000..d5e9b92
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
new file mode 100644
index 0000000..49436ba
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ዝጋ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ዘርጋ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ቅንብሮች"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"ምናሌ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> በስዕል-ላይ-ስዕል ውስጥ ነው"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ይህን ባህሪ እንዲጠቀም ካልፈለጉ ቅንብሮችን ለመክፈት መታ ያድርጉና ያጥፉት።"</string>
+ <string name="pip_play" msgid="3496151081459417097">"አጫውት"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ባለበት አቁም"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ወደ ቀጣይ ዝለል"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ወደ ቀዳሚ ዝለል"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"መጠን ይቀይሩ"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"መተግበሪያ በሁለተኛ ማሳያዎች ላይ ማስጀመርን አይደግፍም።"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"የተከፈለ የማያ ገጽ ከፋይ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"የግራ ሙሉ ማያ ገጽ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ግራ 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ግራ 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ግራ 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"የቀኝ ሙሉ ማያ ገጽ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"የላይ ሙሉ ማያ ገጽ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ከላይ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ከላይ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ከላይ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"የታች ሙሉ ማያ ገጽ"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"ቅንብሮች ለ <xliff:g id="APP_NAME">%1$s</xliff:g> አረፋዎች"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ትርፍ ፍሰት"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ወደ ቁልል መልሰው ያክሉ"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ከ<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ከ <xliff:g id="APP_NAME">%2$s</xliff:g> እና <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ተጨማሪ"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ወደ ላይኛው ግራ አንቀሳቅስ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"የግርጌውን ግራ አንቀሳቅስ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"አረፋን አሰናብት"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ውይይቶችን በአረፋ አታሳይ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"አረፋዎችን በመጠቀም ይወያዩ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"አዲስ ውይይቶች እንደ ተንሳፋፊ አዶዎች ወይም አረፋዎች ሆነው ይታያሉ። አረፋን ለመክፈት መታ ያድርጉ። ለመውሰድ ይጎትቱት።"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"በማንኛውም ጊዜ አረፋዎችን ይቆጣጠሩ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"የዚህ መተግበሪያ አረፋዎችን ለማጥፋት አቀናብርን መታ ያድርጉ"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ምንም የቅርብ ጊዜ አረፋዎች የሉም"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"የቅርብ ጊዜ አረፋዎች እና የተሰናበቱ አረፋዎች እዚህ ብቅ ይላሉ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
new file mode 100644
index 0000000..1661882
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
new file mode 100644
index 0000000..3e81255
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"إغلاق"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"توسيع"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"الإعدادات"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"القائمة"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> يظهر في صورة داخل صورة"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"إذا كنت لا تريد أن يستخدم <xliff:g id="NAME">%s</xliff:g> هذه الميزة، فانقر لفتح الإعدادات، ثم أوقِف تفعيل هذه الميزة."</string>
+ <string name="pip_play" msgid="3496151081459417097">"تشغيل"</string>
+ <string name="pip_pause" msgid="690688849510295232">"إيقاف مؤقت"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"لا يمكن تشغيل التطبيق على شاشات عرض ثانوية."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"أداة تقسيم الشاشة"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"عرض النافذة اليسرى بملء الشاشة"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ضبط حجم النافذة اليسرى ليكون ٧٠%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ضبط حجم النافذة اليسرى ليكون ٥٠%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ضبط حجم النافذة اليسرى ليكون ٣٠%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"عرض النافذة اليمنى بملء الشاشة"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"عرض النافذة العلوية بملء الشاشة"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ضبط حجم النافذة العلوية ليكون ٧٠%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ضبط حجم النافذة العلوية ليكون ٥٠%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ضبط حجم النافذة العلوية ليكون ٣٠%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"عرض النافذة السفلية بملء الشاشة"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"إعدادات فقاعات المحادثات على <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"القائمة الكاملة"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"إضافة دعم إلى الحزم"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> من <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> من <xliff:g id="APP_NAME">%2$s</xliff:g> و<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> أيضًا"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"نقل إلى أعلى يمين الشاشة"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"الانتقال إلى أعلى اليسار"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نقل إلى أسفل يمين الشاشة"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نقل إلى أسفل اليسار"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"إغلاق فقاعة المحادثة"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"عدم عرض المحادثة كفقاعة محادثة"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"الدردشة باستخدام فقاعات المحادثات"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"تظهر المحادثات الجديدة كرموز عائمة أو كفقاعات. انقر لفتح فقاعة المحادثة، واسحبها لتحريكها."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"التحكّم في فقاعات المحادثات في أي وقت"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"انقر على \"إدارة\" لإيقاف فقاعات المحادثات من هذا التطبيق."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ليس هناك فقاعات محادثات"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ستظهر هنا أحدث فقاعات المحادثات وفقاعات المحادثات التي تم إغلاقها."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
new file mode 100644
index 0000000..61588a0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ليس هناك عنوان للبرنامج)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"إغلاق PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
new file mode 100644
index 0000000..81cdc7f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ কৰক"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"বিস্তাৰ কৰক"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিংসমূহ"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"মেনু"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> চিত্ৰৰ ভিতৰৰ চিত্ৰত আছে"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিংসমূহ খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
+ <string name="pip_play" msgid="3496151081459417097">"প্লে কৰক"</string>
+ <string name="pip_pause" msgid="690688849510295232">"পজ কৰক"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"আকাৰ সলনি কৰক"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীণৰ বিভাজক"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"বাওঁফালৰ স্ক্ৰীণখন ৭০% কৰক"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীণখন ৫০% কৰক"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীণখন ৩০% কৰক"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"তলৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিংসমূহ"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"অভাৰফ্ল’"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ষ্টেকত পুনৰ যোগ দিয়ক"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> আৰু<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>টাৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"বুটামটো বাওঁফালে নিয়ক"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিংসমূহ"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন বাৰ্তালাপ উপঙি থকা চিহ্নসমূহ অথবা bubbles হিচাপে প্ৰদর্শিত হয়। Bubbles খুলিবলৈ টিপক। এইটো স্থানান্তৰ কৰিবলৈ টানি নিয়ক।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"যিকোনো সময়তে bubbles নিয়ন্ত্ৰণ কৰক"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"এই এপ্টোৰ পৰা bubbles অফ কৰিবলৈ পৰিচালনা কৰকত টিপক"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনো শেহতীয়া bubbles নাই"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"শেহতীয়া bubbles আৰু অগ্ৰাহ্য কৰা bubbles ইয়াত প্ৰদর্শিত হ\'ব"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
new file mode 100644
index 0000000..c4e3f38
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীণ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
new file mode 100644
index 0000000..7e8590f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Bağlayın"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Genişləndirin"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> şəkil içində şəkildədir"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> tətbiqinin bu funksiyadan istifadə etməyini istəmirsinizsə, ayarları açmaq və deaktiv etmək üçün klikləyin."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Oxudun"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Fasilə verin"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Növbətiyə keçin"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Əvvəlkinə keçin"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ölçüsünü dəyişin"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcısı"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Sol tam ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Sol 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sol 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Sol 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Sağ tam ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Yuxarı tam ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Yuxarı 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aşağı tam ekran"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> yumrucuqları üçün ayarlar"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kənara çıxma"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yenidən dəstəyə əlavə edin"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> tətbiqindən <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> tətbiqindən <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> və daha <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> qabarcıq"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Yuxarıya sola köçürün"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya sağa köçürün"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Aşağıya sola köçürün"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Aşağıya sağa köçürün"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Yumrucuğu ləğv edin"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Söhbəti yumrucuqda göstərmə"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Yumrucuqlardan istifadə edərək söhbət edin"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni söhbətlər üzən nişanlar və ya yumrucuqlar kimi görünür. Yumrucuğu açmaq üçün toxunun. Hərəkət etdirmək üçün sürüşdürün."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Yumrucuqları istənilən vaxt idarə edin"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bu tətbiqdə yumrucuqları deaktiv etmək üçün \"İdarə edin\" seçiminə toxunun"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Yumrucuqlar yoxdur"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son yumrucuqlar və buraxılmış yumrucuqlar burada görünəcək"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
new file mode 100644
index 0000000..58e9d10
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıqsız proqram)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP bağlayın"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..9b2c49a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Podešavanja"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da biste otvorili podešavanja i isključili je."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Pusti"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdelnik podeljenog ekrana"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Režim celog ekrana za levi ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi ekran 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi ekran 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Levi ekran 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Režim celog ekrana za donji ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Režim celog ekrana za gornji ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gornji ekran 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji ekran 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji ekran 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Režim celog ekrana za donji ekran"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Podešavanja za <xliff:g id="APP_NAME">%1$s</xliff:g> oblačiće"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Preklapanje"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj ponovo u grupu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premesti gore levo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ćaskajte u oblačićima"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolišite oblačiće u bilo kom trenutku"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljajte da biste isključili oblačiće iz ove aplikacije"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbačeni oblačići"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
new file mode 100644
index 0000000..d341fd2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
new file mode 100644
index 0000000..b42e2ce
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Закрыць"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Разгарнуць"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Налады"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> з’яўляецца відарысам у відарысе"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Калі вы не хочаце, каб праграма <xliff:g id="NAME">%s</xliff:g> выкарыстоўвала гэту функцыю, дакраніцеся, каб адкрыць налады і адключыць яе."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Прайграць"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Прыпыніць"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Перайсці да наступнага"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Перайсці да папярэдняга"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змяніць памер"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Праграма не падтрымлівае запуск на дадатковых экранах."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Раздзяляльнік падзеленага экрана"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Левы экран – поўнаэкранны рэжым"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Левы экран – 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Левы экран – 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Левы экран – 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Правы экран – поўнаэкранны рэжым"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Верхні экран – поўнаэкранны рэжым"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Верхні экран – 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Верхні экран – 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Верхні экран – 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ніжні экран – поўнаэкранны рэжым"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Налады ўсплывальных апавяшчэнняў у праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Дадатковае меню"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Зноў дадаць у стос"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" і яшчэ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перамясціць лявей і вышэй"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перамясціце правей і вышэй"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перамясціць лявей і ніжэй"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перамясціць правей і ніжэй"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Усплывальныя апавяшчэнні"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя апавяшчэнні. Націсніце, каб адкрыць усплывальнае апавяшчэнне. Перацягніце яго, каб перамясціць."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Кіруйце ўсплывальнымі апавяшчэннямі ў любы час"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Каб выключыць усплывальныя апавяшчэнні з гэтай праграмы, націсніце \"Кіраваць\""</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных апавяшчэнняў"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя апавяшчэнні будуць паказаны тут"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
new file mode 100644
index 0000000..527b5af
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ва ўвесь экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
new file mode 100644
index 0000000..df25b8f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Затваряне"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Разгъване"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> е в режима „Картина в картината“"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ако не искате <xliff:g id="NAME">%s</xliff:g> да използва тази функция, докоснете, за да отворите настройките, и я изключете."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Пускане"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Поставяне на пауза"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Към следващия елемент"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Към предишния елемент"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Преоразмеряване"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Приложението не поддържа използването на алтернативни дисплеи."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Разделител в режима за разделен екран"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ляв екран: Показване на цял екран"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ляв екран: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ляв екран: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ляв екран: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Десен екран: Показване на цял екран"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Горен екран: Показване на цял екран"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Горен екран: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Горен екран: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Горен екран: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Долен екран: Показване на цял екран"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Настройки за балончетата за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Препълване"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Добавяне обратно към стека"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> от <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ от<xliff:g id="APP_NAME">%2$s</xliff:g> и още <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Преместване горе вляво"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Преместване горе вдясно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Преместване долу вляво"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Преместване долу вдясно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отхвърляне на балончетата"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Без балончета за разговора"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Чат с балончета"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори се показват като плаващи икони, или балончета. Докоснете балонче, за да го отворите, или го плъзнете, за да го преместите."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Управление на балончетата по всяко време"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Докоснете „Управление“, за да изключите балончетата от това приложение"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма скорошни балончета"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Скорошните и отхвърлените балончета ще се показват тук"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
new file mode 100644
index 0000000..388eb84
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без заглавие)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
new file mode 100644
index 0000000..e92f8c9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ করুন"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"বড় করুন"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"সেটিংস"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"মেনু"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"ছবির-মধ্যে-ছবি তে <xliff:g id="NAME">%s</xliff:g> আছেন"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> কে এই বৈশিষ্ট্যটি ব্যবহার করতে দিতে না চাইলে ট্যাপ করে সেটিংসে গিয়ে সেটি বন্ধ করে দিন।"</string>
+ <string name="pip_play" msgid="3496151081459417097">"চালান"</string>
+ <string name="pip_pause" msgid="690688849510295232">"বিরাম দিন"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"রিসাইজ করুন"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"সেকেন্ডারি ডিসপ্লেতে অ্যাপ লঞ্চ করা যাবে না।"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"বিভক্ত-স্ক্রিন বিভাজক"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাঁ দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"৭০% বাকি আছে"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"৫০% বাকি আছে"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"৩০% বাকি আছে"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ডান দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"উপর দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ ৭০%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ ৫০%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ ৩০%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"নীচের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> বাবলের জন্য সেটিংস"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ওভারফ্লো"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"স্ট্যাকে আবার যোগ করুন"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> অ্যাপ থেকে <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> অ্যাপ এবং আরও <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>টি থেকে <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"উপরে বাঁদিকে সরান"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"উপরে ডানদিকে সরান"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"নিচে বাঁদিকে সরান"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"নিচে ডান দিকে সরান"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> সেটিংস"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল খারিজ করুন"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"কথোপকথন বাবল হিসেবে দেখাবে না"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"বাবল ব্যবহার করে চ্যাট করুন"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন কথোপকথন ভেসে থাকা আইকন বা বাবল হিসেবে দেখানো হয়। বাবল খুলতে ট্যাপ করুন। সেটি সরাতে ধরে টেনে আনুন।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"যেকোনও সময় বাবল নিয়ন্ত্রণ করুন"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"এই অ্যাপ থেকে বাবল বন্ধ করতে \'ম্যানেজ করুন\' বিকল্প ট্যাপ করুন"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনও সাম্প্রতিক বাবল নেই"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"সাম্প্রতিক ও বাতিল করা বাবল এখানে দেখা যাবে"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
new file mode 100644
index 0000000..783cb92
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিরোনামহীন প্রোগ্রাম)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
new file mode 100644
index 0000000..938e8b5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je u načinu priakza Slika u slici"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da otvorite postavke i isključite je."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduciraj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeći"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodni"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik ekrana"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevo cijeli ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Lijevo 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevo 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Lijevo 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desno cijeli ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Gore cijeli ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gore 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gore 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gore 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Donji ekran kao cijeli ekran"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Postavke za oblačiće aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Preklapanje"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj nazad u grupu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> od aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"Obavještenje <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Pomjeri gore lijevo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblačićima"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatajte koristeći oblačiće"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da otvorite oblačić. Prevucite da ga premjestite."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Upravljajte oblačićima u svakom trenutku"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljaj da isključite oblačiće iz ove aplikacije"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
new file mode 100644
index 0000000..6845ef46
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
new file mode 100644
index 0000000..9cfdc55
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Tanca"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Desplega"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configuració"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> està en pantalla en pantalla"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Si no vols que <xliff:g id="NAME">%s</xliff:g> utilitzi aquesta funció, toca per obrir la configuració i desactiva-la."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reprodueix"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Posa en pausa"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Ves al següent"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Torna a l\'anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Canvia la mida"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalles"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla esquerra completa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Pantalla esquerra al 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pantalla esquerra al 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Pantalla esquerra al 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla dreta completa"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Pantalla superior al 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Pantalla superior al 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Pantalla superior al 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configuració de les bombolles: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú addicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Torna a afegir a la pila"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>) i <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> més"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mou a dalt a l\'esquerra"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Xateja amb bombolles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les converses noves es mostren com a icones flotants o bombolles. Toca per obrir una bombolla. Arrossega-la per moure-la."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla les bombolles en qualsevol moment"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestiona per desactivar les bombolles d\'aquesta aplicació"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hi ha bombolles recents"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bombolles recents i les ignorades es mostraran aquí"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
new file mode 100644
index 0000000..ba89b0c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sense títol)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Tanca PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
new file mode 100644
index 0000000..973a5f9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zavřít"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Rozbalit"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nastavení"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Nabídka"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"Aplikace <xliff:g id="NAME">%s</xliff:g> je v režimu obraz v obraze"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Pokud nechcete, aby aplikace <xliff:g id="NAME">%s</xliff:g> tuto funkci používala, klepnutím otevřete nastavení a funkci vypněte."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Přehrát"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pozastavit"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Přeskočit na další"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Přeskočit na předchozí"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Změnit velikost"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Čára rozdělující obrazovku"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levá část na celou obrazovku"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % vlevo"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % vlevo"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % vlevo"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pravá část na celou obrazovku"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Horní část na celou obrazovku"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % nahoře"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % nahoře"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % nahoře"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolní část na celou obrazovku"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Nastavení bublin aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Rozbalovací nabídka"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Přidat zpět do sady"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"Oznámení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikace <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikace <xliff:g id="APP_NAME">%2$s</xliff:g> a dalších (<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>)"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Přesunout vlevo nahoru"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Přesunout vpravo nahoru"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Přesunout vlevo dolů"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Přesunout vpravo dolů"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavřít bublinu"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci v bublinách"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatujte pomocí bublin"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzace se zobrazují jako plovoucí ikony, neboli bubliny. Klepnutím bublinu otevřete. Přetažením ji posunete."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Nastavení bublin můžete kdykoli upravit"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bubliny pro tuto aplikaci můžete vypnout klepnutím na Spravovat"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žádné nedávné bubliny"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Zde se budou zobrazovat nedávné bubliny a zavřené bubliny"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
new file mode 100644
index 0000000..c75ee13
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Bez názvu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Ukončit obraz v obraze (PIP)"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
new file mode 100644
index 0000000..cdde84c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Luk"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Udvid"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Indstillinger"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> vises som integreret billede"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Hvis du ikke ønsker, at <xliff:g id="NAME">%s</xliff:g> skal benytte denne funktion, kan du åbne indstillingerne og deaktivere den."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Afspil"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Sæt på pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Gå videre til næste"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Gå til forrige"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Rediger størrelse"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke åbnes på sekundære skærme."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Adskiller til opdelt skærm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vis venstre del i fuld skærm"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Venstre 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Venstre 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Venstre 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Vis højre del i fuld skærm"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Vis øverste del i fuld skærm"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Øverste 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Øverste 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Øverste 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vis nederste del i fuld skærm"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Indstillinger for <xliff:g id="APP_NAME">%1$s</xliff:g>-bobler"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overløb"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Føj til stak igen"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> andre"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Flyt op til venstre"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til højre"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til højre"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat ved hjælp af bobler"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som svævende ikoner eller bobler. Tryk for at åbne boblen. Træk for at flytte den."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Styr bobler når som helst"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tryk på Administrer for at deaktivere bobler fra denne app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen seneste bobler"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nye bobler og afviste bobler vises her"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
new file mode 100644
index 0000000..edf10d7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uden titel)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Luk integreret billede"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
new file mode 100644
index 0000000..4979a40
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Schließen"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Maximieren"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Wenn du nicht möchtest, dass <xliff:g id="NAME">%s</xliff:g> diese Funktion verwendet, tippe, um die Einstellungen zu öffnen und die Funktion zu deaktivieren."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Wiedergeben"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausieren"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bildschirmteiler"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vollbild links"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % links"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % links"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % links"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Vollbild rechts"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Vollbild oben"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % oben"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Einstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g>-Bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Mehr anzeigen"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Wieder dem Stapel hinzufügen"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> von <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aus <xliff:g id="APP_NAME">%2$s</xliff:g> und <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> weiteren"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Nach oben links verschieben"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach rechts oben verschieben"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
new file mode 100644
index 0000000..0432f1c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Kein Sendungsname gefunden)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
new file mode 100644
index 0000000..2fa31f0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Κλείσιμο"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Ανάπτυξη"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ρυθμίσεις"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Μενού"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"Η λειτουργία picture-in-picture είναι ενεργή σε <xliff:g id="NAME">%s</xliff:g>."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Εάν δεν θέλετε να χρησιμοποιείται αυτή η λειτουργία από την εφαρμογή <xliff:g id="NAME">%s</xliff:g>, πατήστε για να ανοίξετε τις ρυθμίσεις και απενεργοποιήστε την."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Αναπαραγωγή"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Παύση"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Μετάβαση στο επόμενο"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Μετάβαση στο προηγούμενο"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Αλλαγή μεγέθους"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Η εφαρμογή δεν υποστηρίζει την εκκίνηση σε δευτερεύουσες οθόνες."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Διαχωριστικό οθόνης"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Αριστερή πλήρης οθόνη"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Αριστερή 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Αριστερή 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Αριστερή 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Δεξιά πλήρης οθόνη"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Πάνω πλήρης οθόνη"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Πάνω 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Πάνω 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Πάνω 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Κάτω πλήρης οθόνη"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ρυθμίσεις για συννεφάκια <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Υπερχείλιση"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Προσθήκη ξανά στη στοίβα"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> από <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> από την εφαρμογή <xliff:g id="APP_NAME">%2$s</xliff:g> και <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ακόμη"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Μετακίνηση επάνω αριστερά"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Μετακίνηση επάνω δεξιά"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Μετακίνηση κάτω αριστερά"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Μετακίνηση κάτω δεξιά"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Παράβλ. για συννεφ."</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτήν την εφαρμογή."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Δεν υπάρχουν πρόσφατα συννεφάκια"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Τα πρόσφατα συννεφάκια και τα συννεφάκια που παραβλέψατε θα εμφανίζονται εδώ."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
new file mode 100644
index 0000000..7e4466a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..3a790ec
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
new file mode 100644
index 0000000..d1d8141
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..3a790ec
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
new file mode 100644
index 0000000..d1d8141
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..3a790ec
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
new file mode 100644
index 0000000..d1d8141
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..3a790ec
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
new file mode 100644
index 0000000..d1d8141
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..d8b5b400
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split-screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Exit one-handed mode"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles anytime"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Got it"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
new file mode 100644
index 0000000..3f9ef0e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..2cac751
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Cerrar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está en modo de Pantalla en pantalla"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Si no quieres que <xliff:g id="NAME">%s</xliff:g> use esta función, presiona para abrir la configuración y desactivarla."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Siguiente"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar el tamaño"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La app no puede iniciarse en pantallas secundarias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Izquierda: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Izquierda: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla derecha completa"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Superior: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configuración para burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú ampliado"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a agregar a la pila"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> y <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> más"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Ubicar arriba a la izquierda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbujas"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Presiona Administrar para desactivar las burbujas de esta app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
new file mode 100644
index 0000000..b275b6c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sin título de programa)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
new file mode 100644
index 0000000..7b5045a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Cerrar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Mostrar"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ajustes"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está en imagen en imagen"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Si no quieres que <xliff:g id="NAME">%s</xliff:g> utilice esta función, toca la notificación para abrir los ajustes y desactivarla."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltar al siguiente"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Volver al anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La aplicación no se puede abrir en pantallas secundarias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Dividir la pantalla"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Izquierda 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Izquierda 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla derecha completa"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Superior 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> y <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> más"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover arriba a la izquierda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca para abrir la burbuja. Arrastra para moverla."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestionar para desactivar las burbujas de esta aplicación"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las cerradas aparecerán aquí"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
new file mode 100644
index 0000000..6a6ad13
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sin título)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
new file mode 100644
index 0000000..2a261db
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Sule"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Laiendamine"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Seaded"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menüü"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> on režiimis Pilt pildis"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Kui te ei soovi, et rakendus <xliff:g id="NAME">%s</xliff:g> seda funktsiooni kasutaks, puudutage seadete avamiseks ja lülitage see välja."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Esita"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Peata"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Järgmise juurde"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Eelmise juurde"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Suuruse muutmine"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ekraanijagaja"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasak täisekraan"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vasak: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasak: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vasak: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Parem täisekraan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ülemine täisekraan"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Ülemine: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ülemine: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ülemine: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alumine täisekraan"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> mullide seaded"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ületäide"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Lisa tagasi virna"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> rakendusest <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> rakenduselt <xliff:g id="APP_NAME">%2$s</xliff:g> ja veel <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Teisalda üles vasakule"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ära kuva vestlust mullina"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Vestelge mullide abil"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Uued vestlused kuvatakse hõljuvate ikoonidena ehk mullidena. Puudutage mulli avamiseks. Lohistage mulli, et seda liigutada."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Juhtige mulle igal ajal"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Selle rakenduse puhul mullide väljalülitamiseks puudutage valikut Halda"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hiljutisi mulle pole"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Siin kuvatakse hiljutised ja suletud mullid."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
new file mode 100644
index 0000000..26cfae2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programmi pealkiri puudub)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
new file mode 100644
index 0000000..9167e8e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Itxi"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Zabaldu"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ezarpenak"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menua"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"Pantaila txiki gainjarrian dago <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikazioa ezin da abiarazi bigarren mailako pantailatan."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Pantaila-zatitzailea"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ezarri ezkerraldea pantaila osoan"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ezarri ezkerraldea % 70en"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ezarri ezkerraldea % 50en"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ezarri ezkerraldea % 30en"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ezarri eskuinaldea pantaila osoan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ezarri goialdea pantaila osoan"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Ezarri goialdea % 70en"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ezarri goialdea % 50en"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ezarri goialdea % 30en"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ezarri behealdea pantaila osoan"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren ezarpenen burbuilak"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Gainezkatzea"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Gehitu berriro errenkadan"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioaren \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" jakinarazpena, eta beste <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Eraman goialdera, ezkerretara"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Txateatu burbuilen bidez"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Elkarrizketa berriak ikono gainerakor edo burbuila gisa agertzen dira. Sakatu burbuila irekitzeko. Arrasta ezazu mugitzeko."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolatu burbuilak edonoiz"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Aplikazioaren burbuilak desaktibatzeko, sakatu Kudeatu"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
new file mode 100644
index 0000000..985677f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa izengabea)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
new file mode 100644
index 0000000..3f566bf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"بستن"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"بزرگ کردن"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"تنظیمات"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"منو"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> درحالت تصویر در تصویر است"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"اگر نمیخواهید <xliff:g id="NAME">%s</xliff:g> از این قابلیت استفاده کند، با ضربه زدن، تنظیمات را باز کنید و آن را خاموش کنید."</string>
+ <string name="pip_play" msgid="3496151081459417097">"پخش"</string>
+ <string name="pip_pause" msgid="690688849510295232">"توقف موقت"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"رد شدن به بعدی"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"رد شدن به قبلی"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغییر اندازه"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمیکند."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"برنامه از راهاندازی در نمایشگرهای ثانویه پشتیبانی نمیکند."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"تقسیمکننده صفحه"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"تمامصفحه چپ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"٪۷۰ چپ"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"٪۵۰ چپ"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"٪۳۰ چپ"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"تمامصفحه راست"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"تمامصفحه بالا"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"٪۷۰ بالا"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"٪۳۰ بالا"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"تمامصفحه پایین"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"تنظیمات برای حبابکهای <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"لبریزشده"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"افزودن برگشت به پشته"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g> و <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> مورد بیشتر"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"انتقال به بالا سمت راست"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"انتقال به بالا سمت چپ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"انتقال به پایین سمت راست"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"انتقال به پایین سمت چپ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابکها"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها ضربه بزنید. برای جابهجایی، آن را بکشید."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابکها در هرزمانی"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابکها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکها اخیر و حبابکها ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
new file mode 100644
index 0000000..c17dc33
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"بستن PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
new file mode 100644
index 0000000..0aa4b40
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Sulje"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Laajenna"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Asetukset"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Valikko"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> on kuva kuvassa ‑tilassa"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Jos et halua, että <xliff:g id="NAME">%s</xliff:g> voi käyttää tätä ominaisuutta, avaa asetukset napauttamalla ja poista se käytöstä."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Toista"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Keskeytä"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Siirry seuraavaan"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Siirry edelliseen"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Muuta kokoa"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Näytön jakaja"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasen koko näytölle"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vasen 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasen 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vasen 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Oikea koko näytölle"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Yläosa koko näytölle"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Yläosa 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yläosa 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yläosa 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alaosa koko näytölle"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Kuplien asetukset: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ylivuoto"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Lisää takaisin pinoon"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>) ja <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> muuta"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Siirrä vasempaan yläreunaan"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Siirrä oikeaan yläreunaan"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Siirrä vasempaan alareunaan"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Siirrä oikeaan alareunaan"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Älä näytä kuplia keskusteluista"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chattaile kuplien avulla"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Uudet keskustelut näkyvät kelluvina kuvakkeina tai kuplina. Avaa kupla napauttamalla. Siirrä sitä vetämällä."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Muuta kuplien asetuksia milloin tahansa"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Valitse Ylläpidä, jos haluat poistaa kuplat käytöstä tästä sovelluksesta"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ei viimeaikaisia kuplia"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viimeaikaiset ja äskettäin ohitetut kuplat näkyvät täällä"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
new file mode 100644
index 0000000..55c1680
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nimetön)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..6a50593
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Fermer"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Développer"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Paramètres"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> est en mode d\'incrustation d\'image"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Si vous ne voulez pas que <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalité, touchez l\'écran pour ouvrir les paramètres, puis désactivez-la."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Lire"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Interrompre"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au suivant"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Revenir au précédent"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Plein écran à la gauche"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % à la gauche"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % à la gauche"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % à la gauche"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Plein écran à la droite"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Plein écran dans le haut"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % dans le haut"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % dans le haut"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % dans le haut"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Plein écran dans le bas"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres pour les bulles de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu déroulant"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Replacer sur la pile"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> autres"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Déplacer dans coin sup. gauche"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer dans coin sup. droit"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer dans coin inf. gauche"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer dans coin inf. droit"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Clavarder en utilisant des bulles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Paramètres des bulles"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toucher Gérer pour désactiver les bulles de cette application"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et les bulles ignorées s\'afficheront ici"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
new file mode 100644
index 0000000..5f813e6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Aucun programme de titre)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fermer mode IDI"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
new file mode 100644
index 0000000..fd6e743
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Fermer"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Développer"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Paramètres"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> est en mode Picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Si vous ne voulez pas que l\'application <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalité, appuyez ici pour ouvrir les paramètres et la désactiver."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Lecture"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Suspendre"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au contenu suivant"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Passer au contenu précédent"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Écran de gauche en plein écran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Écran de gauche à 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Écran de gauche à 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Écran de gauche à 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Écran de droite en plein écran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Écran du haut en plein écran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Écran du haut à 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Écran du haut à 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Écran du haut à 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Écran du bas en plein écran"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Dépassement"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Ajouter à nouveau l\'élément à la pile"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de l\'application <xliff:g id="APP_NAME">%2$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> autres"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Déplacer en haut à gauche"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôler les paramètres des bulles"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Appuyez sur \"Gérer\" pour désactiver les bulles de cette application"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et ignorées s\'afficheront ici"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
new file mode 100644
index 0000000..52d942d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programme sans titre)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fermer mode PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
new file mode 100644
index 0000000..057881b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Pechar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Despregar"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está na pantalla superposta"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Se non queres que <xliff:g id="NAME">%s</xliff:g> utilice esta función, toca a configuración para abrir as opcións e desactivar a función."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Ir ao seguinte"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Ir ao anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A aplicación non se pode iniciar en pantallas secundarias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla completa á esquerda"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % á esquerda"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % á esquerda"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % á esquerda"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla completa á dereita"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla completa arriba"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % arriba"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % arriba"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % arriba"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla completa abaixo"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configuración das burbullas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Mostrar menú adicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Engadir de novo á pilla"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> e <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> máis"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover á parte super. esquerda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover á parte superior dereita"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover á parte infer. esquerda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover á parte inferior dereita"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatear usando burbullas"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"As conversas novas aparecen como iconas flotantes ou burbullas. Toca para abrir a burbulla e arrastra para movela."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla as burbullas"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Para desactivar as burbullas nesta aplicación, toca Xestionar"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Non hai burbullas recentes"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"As burbullas recentes e ignoradas aparecerán aquí."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
new file mode 100644
index 0000000..a896d88
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sen título)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Pechar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
new file mode 100644
index 0000000..d9950aa
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"બંધ કરો"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"વિસ્તૃત કરો"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"સેટિંગ"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"મેનૂ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ચિત્રમાં-ચિત્રની અંદર છે"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"જો તમે નથી ઇચ્છતા કે <xliff:g id="NAME">%s</xliff:g> આ સુવિધાનો ઉપયોગ કરે, તો સેટિંગ ખોલવા માટે ટૅપ કરો અને તેને બંધ કરો."</string>
+ <string name="pip_play" msgid="3496151081459417097">"ચલાવો"</string>
+ <string name="pip_pause" msgid="690688849510295232">"થોભાવો"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"આગલા પર જાઓ"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"પહેલાંના પર જાઓ"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"કદ બદલો"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર લૉન્ચનું સમર્થન કરતી નથી."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"સ્પ્લિટ-સ્ક્રીન વિભાજક"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ડાબી પૂર્ણ સ્ક્રીન"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ડાબે 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ડાબે 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ડાબે 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"જમણી સ્ક્રીન સ્ક્રીન"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"શીર્ષ પૂર્ણ સ્ક્રીન"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"શીર્ષ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"શીર્ષ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"શીર્ષ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"તળિયાની પૂર્ણ સ્ક્રીન"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> બબલ માટેનાં સેટિંગ"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ઓવરફ્લો"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"સ્ટૅકમાં ફરી ઉમેરો"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> તરફથી <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> અને વધુ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> તરફથી <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ઉપર ડાબે ખસેડો"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ઉપર જમણે ખસેડો"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"નીચે ડાબે ખસેડો"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"નીચે જમણે ખસેડો"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચેટ કરો"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"બબલને કોઈપણ સમયે નિયંત્રિત કરો"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"આ ઍપમાંથી બબલને બંધ કરવા માટે મેનેજ કરો પર ટૅપ કરો"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"તાજેતરના કોઈ બબલ નથી"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"એકદમ નવા બબલ અને છોડી દીધેલા બબલ અહીં દેખાશે"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
new file mode 100644
index 0000000..a5c0c31
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
new file mode 100644
index 0000000..b1117bd
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"बंद करें"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"विस्तार करें"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"मेन्यू"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"पिक्चर में पिक्चर\" के अंदर है"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"अगर आप नहीं चाहते कि <xliff:g id="NAME">%s</xliff:g> इस सुविधा का उपयोग करे, तो सेटिंग खोलने के लिए टैप करें और उसे बंद करें ."</string>
+ <string name="pip_play" msgid="3496151081459417097">"चलाएं"</string>
+ <string name="pip_pause" msgid="690688849510295232">"रोकें"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"अगले पर जाएं"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"पिछले पर जाएं"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदलें"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ऐप विभाजित स्क्रीन का समर्थन नहीं करता है."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर ऐप लॉन्च नहीं किया जा सकता."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"विभाजित स्क्रीन विभाजक"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बाईं स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"बाईं स्क्रीन को 70% बनाएं"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"बाईं स्क्रीन को 50% बनाएं"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"बाईं स्क्रीन को 30% बनाएं"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"दाईं स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ऊपर की स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ऊपर की स्क्रीन को 70% बनाएं"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ऊपर की स्क्रीन को 50% बनाएं"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ऊपर की स्क्रीन को 30% बनाएं"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"नीचे की स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> बबल्स की सेटिंग"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ओवरफ़्लो"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"स्टैक में वापस जोड़ें"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> से <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> और <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> अन्य ऐप्लिकेशन से <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"सबसे ऊपर बाईं ओर ले जाएं"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सबसे ऊपर दाईं ओर ले जाएं"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"बाईं ओर सबसे नीचे ले जाएं"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"सबसे नीचे दाईं ओर ले जाएं"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> की सेटिंग"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारिज करें"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"बातचीत को बबल न करें"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल्स का इस्तेमाल करके चैट करें"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"नई बातचीत फ़्लोटिंग आइकॉन या बबल्स की तरह दिखेंगी. बबल को खोलने के लिए टैप करें. इसे एक जगह से दूसरी जगह ले जाने के लिए खींचें और छोड़ें."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"जब चाहें, बबल्स को कंट्रोल करें"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"इस ऐप्लिकेशन पर बबल्स को बंद करने के लिए \'प्रबंधित करें\' पर टैप करें"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
new file mode 100644
index 0000000..c2131eb
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्क्रीन"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
new file mode 100644
index 0000000..0f617f3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Proširivanje"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Izbornik"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> jest na slici u slici"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da aplikacija <xliff:g id="NAME">%s</xliff:g> upotrebljava tu značajku, dodirnite da biste otvorili postavke i isključili je."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduciraj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeće"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodno"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik podijeljenog zaslona"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevi zaslon u cijeli zaslon"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Lijevi zaslon na 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevi zaslon na 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Lijevi zaslon na 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desni zaslon u cijeli zaslon"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Gornji zaslon u cijeli zaslon"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gornji zaslon na 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji zaslon na 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji zaslon na 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Donji zaslon u cijeli zaslon"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Postavke za oblačiće za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Dodatno"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodajte natrag u nizove"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premjesti u gornji lijevi kut"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblačićima"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Oblačići u chatu"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori pojavljuju se kao pomične ikone ili oblačići. Dodirnite za otvaranje oblačića. Povucite da biste ga premjestili."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Upravljanje oblačićima u svakom trenutku"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljanje da biste isključili oblačiće iz ove aplikacije"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovdje će se prikazivati nedavni i odbačeni oblačići"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
new file mode 100644
index 0000000..76994a5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
new file mode 100644
index 0000000..1fd87e6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Bezárás"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Kibontás"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Beállítások"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"A(z) <xliff:g id="NAME">%s</xliff:g> kép a képben funkciót használ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ha nem szeretné, hogy a(z) <xliff:g id="NAME">%s</xliff:g> használja ezt a funkciót, koppintson a beállítások megnyitásához, és kapcsolja ki."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Lejátszás"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Szüneteltetés"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Ugrás a következőre"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Ugrás az előzőre"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Átméretezés"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Elválasztó az osztott nézetben"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Bal oldali teljes képernyőre"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Bal oldali 70%-ra"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Bal oldali 50%-ra"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Bal oldali 30%-ra"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Jobb oldali teljes képernyőre"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Felső teljes képernyőre"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Felső 70%-ra"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Felső 50%-ra"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Felső 30%-ra"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alsó teljes képernyőre"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g>-buborékok beállításai"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"További elemeket tartalmazó menü"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Visszaküldés a verembe"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> a(z) <xliff:g id="APP_NAME">%2$s</xliff:g> alkalmazásból és <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> további"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Áthelyezés fel és balra"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Áthelyezés fel és jobbra"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Áthelyezés le és balra"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Áthelyezés le és jobbra"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Buborék elvetése"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszélgetés buborékban"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Buborékokat használó csevegés"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Az új beszélgetések lebegő ikonként, vagyis buborékként jelennek meg. A buborék megnyitásához koppintson rá. Áthelyezéshez húzza a kívánt helyre."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Buborékok vezérlése bármikor"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"A Kezelés gombra koppintva kapcsolhatja ki az alkalmazásból származó buborékokat"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nincsenek buborékok a közelmúltból"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"A legutóbbi és az elvetett buborékok itt jelennek majd meg"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
new file mode 100644
index 0000000..5254ccc
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Cím nélküli program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP bezárása"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
new file mode 100644
index 0000000..c374927
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Փակել"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Ընդարձակել"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Կարգավորումներ"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Ընտրացանկ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>-ը «Նկար նկարի մեջ» ռեժիմում է"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Եթե չեք ցանկանում, որ <xliff:g id="NAME">%s</xliff:g>-ն օգտագործի այս գործառույթը, հպեք՝ կարգավորումները բացելու և այն անջատելու համար։"</string>
+ <string name="pip_play" msgid="3496151081459417097">"Նվագարկել"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Դադարեցնել"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Անցնել հաջորդին"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Վերադառնալ նախորդին"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Փոխել չափը"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Հավելվածը չի աջակցում գործարկումը լրացուցիչ էկրանների վրա"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Տրոհված էկրանի բաժանիչ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ձախ էկրանը՝ լիաէկրան"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ձախ էկրանը՝ 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ձախ էկրանը՝ 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ձախ էկրանը՝ 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Աջ էկրանը՝ լիաէկրան"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Վերևի էկրանը՝ լիաէկրան"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Վերևի էկրանը՝ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Վերևի էկրանը՝ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Վերևի էկրանը՝ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ներքևի էկրանը՝ լիաէկրան"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ի ամպիկների կարգավորումներ"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Լրացուցիչ ընտրացանկ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Նորից ավելացնել զտիչներում"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="APP_NAME">%2$s</xliff:g>-ից"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>` <xliff:g id="APP_NAME">%2$s</xliff:g>-ից ու ևս <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ամպիկ"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Տեղափոխել վերև՝ ձախ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Տեղափոխել վերև՝ աջ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Տեղափոխել ներքև՝ ձախ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Տեղափոխել ներքև՝ աջ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Փակել ամպիկը"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Զրույցը չցուցադրել ամպիկի տեսքով"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Զրույցի ամպիկներ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Նոր զրույցները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Ամպիկների կարգավորումներ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Հպեք «Կառավարել» կոճակին՝ այս հավելվածի ամպիկներն անջատելու համար։"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ամպիկներ չկան"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Այստեղ կցուցադրվեն վերջերս օգտագործված և փակված ամպիկները, որոնք կկարողանաք հեշտությամբ վերաբացել"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
new file mode 100644
index 0000000..36680e9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Առանց վերնագրի ծրագիր)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
new file mode 100644
index 0000000..2cb26b4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Luaskan"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Setelan"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Jika Anda tidak ingin <xliff:g id="NAME">%s</xliff:g> menggunakan fitur ini, ketuk untuk membuka setelan dan menonaktifkannya."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Putar"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Jeda"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Lewati ke berikutnya"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Lewati ke sebelumnya"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah ukuran"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Pembagi layar terpisah"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Layar penuh di kiri"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kiri 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kiri 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Layar penuh di kanan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Layar penuh di atas"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Atas 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Atas 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Layar penuh di bawah"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setelan untuk balon <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Tambahan"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Tambahkan kembali ke stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> dari <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> dari <xliff:g id="APP_NAME">%2$s</xliff:g> dan <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> lainnya"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Pindahkan ke kiri atas"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat dalam tampilan balon"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Percakapan baru muncul sebagai ikon mengambang, atau balon. Ketuk untuk membuka balon. Tarik untuk memindahkannya."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrol balon kapan saja"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Ketuk Kelola untuk menonaktifkan balon dari aplikasi ini"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tidak ada balon baru-baru ini"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Balon yang baru dipakai dan balon yang telah ditutup akan muncul di sini"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
new file mode 100644
index 0000000..30544b5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tanpa judul)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Layar penuh"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
new file mode 100644
index 0000000..3b749bd
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Loka"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Stækka"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Stillingar"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Valmynd"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> er með mynd í mynd"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ef þú vilt ekki að <xliff:g id="NAME">%s</xliff:g> noti þennan eiginleika skaltu ýta til að opna stillingarnar og slökkva á því."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Spila"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Gera hlé"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Fara á næsta"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Fara á fyrra"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Breyta stærð"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Forrit styður ekki opnun á öðrum skjá."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skjáskipting"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vinstri á öllum skjánum"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vinstri 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vinstri 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vinstri 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Hægri á öllum skjánum"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Efri á öllum skjánum"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Efri 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Efri 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Efri 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Neðri á öllum skjánum"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Stillingar fyrir blöðrur frá <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Yfirflæði"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Bæta aftur í stafla"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> frá <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ frá <xliff:g id="APP_NAME">%2$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> í viðbót"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Færa efst til vinstri"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Færa efst til hægri"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Færa neðst til vinstri"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Færðu neðst til hægri"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blöðru"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal í blöðru"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Spjalla með blöðrum"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Ný samtöl birtast sem fljótandi tákn eða blöðrur. Ýttu til að opna blöðru. Dragðu hana til að færa."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Hægt er að stjórna blöðrum hvenær sem er"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Ýttu á „Stjórna“ til að slökkva á blöðrum frá þessu forriti"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Engar nýlegar blöðrur"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nýlegar blöðrur og blöðrur sem þú hefur lokað birtast hér"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
new file mode 100644
index 0000000..ab3f3a6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Efni án titils)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Loka mynd í mynd"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
new file mode 100644
index 0000000..bb4f9d7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Chiudi"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Espandi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Impostazioni"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> è in Picture in picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Se non desideri che l\'app <xliff:g id="NAME">%s</xliff:g> utilizzi questa funzione, tocca per aprire le impostazioni e disattivarla."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Riproduci"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Metti in pausa"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Passa ai contenuti successivi"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Passa ai contenuti precedenti"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ridimensiona"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'app non supporta l\'avvio su display secondari."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Strumento per schermo diviso"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Schermata sinistra a schermo intero"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Schermata sinistra al 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Schermata sinistra al 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Schermata sinistra al 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Schermata destra a schermo intero"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Schermata superiore a schermo intero"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Schermata superiore al 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Schermata superiore al 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Schermata superiore al 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Schermata inferiore a schermo intero"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Impostazioni per bolle <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Altre"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Aggiungi di nuovo all\'elenco"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da <xliff:g id="APP_NAME">%2$s</xliff:g> e altre <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Sposta in alto a sinistra"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sposta in alto a destra"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sposta in basso a sinistra"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
new file mode 100644
index 0000000..0d50580
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma senza titolo)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Chiudi PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
new file mode 100644
index 0000000..f163dcf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"הרחב"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
+ <string name="pip_play" msgid="3496151081459417097">"הפעל"</string>
+ <string name="pip_pause" msgid="690688849510295232">"השהה"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"אפשר לדלג אל הבא"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"אפשר לדלג אל הקודם"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"שינוי גודל"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"האפליקציה אינה תומכת בהפעלה במסכים משניים."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"מחלק מסך מפוצל"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"מסך שמאלי מלא"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"שמאלה 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"שמאלה 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"שמאלה 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"מסך ימני מלא"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"מסך עליון מלא"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"עליון 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"עליון 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"עליון 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"מסך תחתון מלא"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות בשביל בועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"גלישה"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"הוספה בחזרה לערימה"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מהאפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מ-<xliff:g id="APP_NAME">%2$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"העברה לפינה השמאלית העליונה"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"העברה לפינה הימנית העליונה"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"העברה לפינה השמאלית התחתונה"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"העברה לפינה הימנית התחתונה"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"סגירת בועה"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"אין להציג בועות לשיחה"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"לדבר בבועות"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"שיחות חדשות מופיעות כסמלים צפים, או בועות. יש להקיש כדי לפתוח בועה. יש לגרור כדי להזיז אותה."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"שליטה בבועות, בכל זמן"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"יש להקיש על \'ניהול\' כדי להשבית את הבועות מהאפליקציה הזו"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"אין בועות מהזמן האחרון"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"בועות אחרונות ובועות שנסגרו יופיעו כאן"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
new file mode 100644
index 0000000..528d557
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"סגור PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
new file mode 100644
index 0000000..c31d01c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"閉じる"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"メニュー"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>はピクチャー イン ピクチャーで表示中です"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>でこの機能を使用しない場合は、タップして設定を開いて OFF にしてください。"</string>
+ <string name="pip_play" msgid="3496151081459417097">"再生"</string>
+ <string name="pip_pause" msgid="690688849510295232">"一時停止"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"次へスキップ"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"前へスキップ"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"サイズ変更"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"アプリはセカンダリ ディスプレイでの起動に対応していません。"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"分割画面の分割線"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"左全画面"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"左 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"左 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"左 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"右全画面"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"上部全画面"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"上 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"上 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"上 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"下部全画面"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> のバブルの設定"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"オーバーフロー"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"スタックに戻す"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>(<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>(<xliff:g id="APP_NAME">%2$s</xliff:g>)、他 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 件"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"左上に移動"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"右上に移動"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"左下に移動"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"右下に移動"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"バブルを閉じる"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近表示されたバブルや閉じたバブルが、ここに表示されます"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
new file mode 100644
index 0000000..4a9cc40
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
new file mode 100644
index 0000000..9e86c8a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"დახურვა"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"გაშლა"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"პარამეტრები"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"მენიუ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> იყენებს რეჟიმს „ეკრანი ეკრანში“"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"თუ არ გსურთ, რომ <xliff:g id="NAME">%s</xliff:g> ამ ფუნქციას იყენებდეს, აქ შეხებით შეგიძლიათ გახსნათ პარამეტრები და გამორთოთ ის."</string>
+ <string name="pip_play" msgid="3496151081459417097">"დაკვრა"</string>
+ <string name="pip_pause" msgid="690688849510295232">"დაპაუზება"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"შემდეგზე გადასვლა"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"წინაზე გადასვლა"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ზომის შეცვლა"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"აპს არ გააჩნია მეორეული ეკრანის მხარდაჭერა."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"გაყოფილი ეკრანის რეჟიმის გამყოფი"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"მარცხენა ნაწილის სრულ ეკრანზე გაშლა"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"მარცხენა ეკრანი — 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"მარცხენა ეკრანი — 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"მარცხენა ეკრანი — 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"მარჯვენა ნაწილის სრულ ეკრანზე გაშლა"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ზედა ნაწილის სრულ ეკრანზე გაშლა"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ზედა ეკრანი — 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ზედა ეკრანი — 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ზედა ეკრანი — 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ქვედა ნაწილის სრულ ეკრანზე გაშლა"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"პარამეტრები <xliff:g id="APP_NAME">%1$s</xliff:g> ბუშტებისთვის"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"გადავსება"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ისევ დამატება დასტაზე"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g>-ისგან"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g>-დან და კიდევ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ზევით და მარცხნივ გადატანა"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"გადაანაცვლეთ ზევით და მარჯვნივ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ქვევით და მარცხნივ გადატანა"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ბუშტის დახურვა"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"აიკრძალოს საუბრის ბუშტები"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ჩეთი ბუშტების გამოყენებით"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ახალი საუბრები გამოჩნდება როგორც მოტივტივე ხატულები ან ბუშტები. შეეხეთ ბუშტის გასახსნელად. გადაიტანეთ ჩავლებით."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ბუშტების ნებისმიერ დროს გაკონტროლება"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ამ აპის ბუშტების გამოსართავად შეეხეთ „მართვას“"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ბოლო დროს გამოყენებული ბუშტები არ არის"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"აქ გამოჩნდება ბოლოდროინდელი ბუშტები და უარყოფილი ბუშტები"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
new file mode 100644
index 0000000..be420a3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
new file mode 100644
index 0000000..0cda01e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Жабу"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Жаю"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Mәзір"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"суреттегі сурет\" режимінде"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> деген пайдаланушының бұл мүмкіндікті пайдалануын қаламасаңыз, параметрлерді түртіп ашыңыз да, оларды өшіріңіз."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Ойнату"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Кідірту"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Келесіге өту"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Алдыңғысына оралу"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлшемін өзгерту"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Қолданба қосымша дисплейлерде іске қосуды қолдамайды."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Бөлінген экран бөлгіші"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Сол жағын толық экранға шығару"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% сол жақта"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% сол жақта"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% сол жақта"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Оң жағын толық экранға шығару"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Жоғарғы жағын толық экранға шығару"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% жоғарғы жақта"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% жоғарғы жақта"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% жоғарғы жақта"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Төменгісін толық экранға шығару"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> қалқыма хабарларының параметрлері"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Қосымша мәзір"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Стекке қайта енгізу"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> жіберген хабарландыру: <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасы жіберген <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> және тағы <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Жоғарғы сол жаққа жылжыту"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жоғары оң жаққа жылжыту"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төменгі сол жаққа жылжыту"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төменгі оң жаққа жылжыту"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Қалқымалы хабарды жабу"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Әңгіменің қалқыма хабары көрсетілмесін"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Қалқыма хабарлар арқылы сөйлесу"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңа әңгімелер қалқыма белгішелер немесе хабарлар түрінде көрсетіледі. Қалқыма хабарды ашу үшін түртіңіз. Жылжыту үшін сүйреңіз."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Қалқыма хабарларды реттеу"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бұл қолданбадан қалқыма хабарларды өшіру үшін \"Басқару\" түймесін түртіңіз."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Жақындағы қалқыма хабарлар жоқ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
new file mode 100644
index 0000000..9b12cff
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
new file mode 100644
index 0000000..d0b1876
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"បិទ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ពង្រីក"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ការកំណត់"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"ម៉ឺនុយ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ស្ថិតក្នុងមុខងាររូបក្នុងរូប"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"ប្រសិនបើអ្នកមិនចង់ឲ្យ <xliff:g id="NAME">%s</xliff:g> ប្រើមុខងារនេះ សូមចុចបើកការកំណត់ រួចបិទវា។"</string>
+ <string name="pip_play" msgid="3496151081459417097">"លេង"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ផ្អាក"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"រំលងទៅបន្ទាប់"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"រំលងទៅក្រោយ"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ប្ដូរទំហំ"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះប្រហែលជាមិនដំណើរការនៅលើអេក្រង់បន្ទាប់បន្សំទេ។"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"កម្មវិធីនេះមិនអាចចាប់ផ្តើមនៅលើអេក្រង់បន្ទាប់បន្សំបានទេ។"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"កម្មវិធីចែកអេក្រង់បំបែក"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"អេក្រង់ពេញខាងឆ្វេង"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ឆ្វេង 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ឆ្វេង 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ឆ្វេង 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"អេក្រង់ពេញខាងស្តាំ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"អេក្រង់ពេញខាងលើ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ខាងលើ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ខាងលើ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ខាងលើ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"អេក្រង់ពេញខាងក្រោម"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"ការកំណត់សម្រាប់ពពុះ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ម៉ឺនុយបន្ថែម"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"បញ្ចូលទៅក្នុងគំនរវិញ"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ពី <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ពី <xliff:g id="APP_NAME">%2$s</xliff:g> និង <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ទៀត"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងឆ្វេង"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងស្ដាំ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ច្រានចោលពពុះ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"កុំបង្ហាញការសន្ទនាជាពពុះ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ជជែកដោយប្រើពពុះ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ការសន្ទនាថ្មីៗបង្ហាញជាពពុះ ឬរូបអណ្ដែត។ ចុច ដើម្បីបើកពពុះ។ អូស ដើម្បីផ្លាស់ទីពពុះនេះ។"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"គ្រប់គ្រងពពុះបានគ្រប់ពេល"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ចុច \"គ្រប់គ្រង\" ដើម្បីបិទពពុះពីកម្មវិធីនេះ"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"មិនមានពពុះថ្មីៗទេ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ពពុះថ្មីៗ និងពពុះដែលបានបិទនឹងបង្ហាញនៅទីនេះ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោលសារលេចឡើង។"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
new file mode 100644
index 0000000..e1a673e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(កម្មវិធីគ្មានចំណងជើង)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
new file mode 100644
index 0000000..456dc06
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ಮುಚ್ಚಿ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ವಿಸ್ತೃತಗೊಳಿಸು"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"ಮೆನು"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವಾಗಿದೆ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ಈ ವೈಶಿಷ್ಟ್ಯ ಬಳಸುವುದನ್ನು ನೀವು ಬಯಸದಿದ್ದರೆ, ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಲು ಮತ್ತು ಅದನ್ನು ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="pip_play" msgid="3496151081459417097">"ಪ್ಲೇ"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ವಿರಾಮಗೊಳಿಸಿ"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ಮುಂದಕ್ಕೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ಹಿಂದಕ್ಕೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"ಸ್ಪ್ಲಿಟ್-ಪರದೆ ಡಿವೈಡರ್"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಪೂರ್ಣ ಪರದೆ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% ಎಡಕ್ಕೆ"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% ಎಡಕ್ಕೆ"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% ಎಡಕ್ಕೆ"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಪೂರ್ಣ ಪರದೆ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಪೂರ್ಣ ಪರದೆ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% ಮೇಲಕ್ಕೆ"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% ಮೇಲಕ್ಕೆ"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% ಮೇಲಕ್ಕೆ"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಬಬಲ್ಸ್ಗಾಗಿ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ಓವರ್ಫ್ಲೋ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ಸ್ಟ್ಯಾಕ್ಗೆ ಪುನಃ ಸೇರಿಸಿ"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> ಆ್ಯಪ್ನ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ಮತ್ತು <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ಹೆಚ್ಚಿನವುಗಳ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ಸ್ಕ್ರೀನ್ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ತೇಲುವ ಐಕಾನ್ಗಳು ಅಥವಾ ಬಬಲ್ಸ್ ಆಗಿ ಗೋಚರಿಸುತ್ತವೆ. ಬಬಲ್ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಅದನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಲು ಎಳೆಯಿರಿ."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಬಬಲ್ಸ್ ಅನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ಈ ಆ್ಯಪ್ನಿಂದ ಬಬಲ್ಸ್ ಅನ್ನು ಆಫ್ ಮಾಡಲು ನಿರ್ವಹಿಸಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಇಲ್ಲ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಮತ್ತು ವಜಾಗೊಳಿಸಿದ ಬಬಲ್ಸ್ ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
new file mode 100644
index 0000000..699824a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
new file mode 100644
index 0000000..57a2cb1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"닫기"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"펼치기"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"설정"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"메뉴"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>에서 PIP 사용 중"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>에서 이 기능이 사용되는 것을 원하지 않는 경우 탭하여 설정을 열고 기능을 사용 중지하세요."</string>
+ <string name="pip_play" msgid="3496151081459417097">"재생"</string>
+ <string name="pip_pause" msgid="690688849510295232">"일시중지"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"다음으로 건너뛰기"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"이전으로 건너뛰기"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"크기 조절"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"앱이 보조 디스플레이에서의 실행을 지원하지 않습니다."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"화면 분할기"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"왼쪽 화면 전체화면"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"왼쪽 화면 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"왼쪽 화면 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"왼쪽 화면 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"오른쪽 화면 전체화면"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"위쪽 화면 전체화면"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"위쪽 화면 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"위쪽 화면 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"위쪽 화면 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"아래쪽 화면 전체화면"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> 대화창 설정"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"더보기"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"스택에 다시 추가"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>의 <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> 외 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>개의 <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"왼쪽 상단으로 이동"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"오른쪽 상단으로 이동"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"왼쪽 하단으로 이동"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"오른쪽 하단으로 이동"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> 설정"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"대화창 닫기"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"대화를 대화창으로 표시하지 않기"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"대화창으로 채팅하기"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"새로운 대화가 플로팅 아이콘인 대화창으로 표시됩니다. 대화창을 열려면 탭하세요. 드래그하여 이동할 수 있습니다."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"언제든지 대화창을 제어하세요"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"이 앱에서 대화창을 사용 중지하려면 관리를 탭하세요."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"최근 대화창 없음"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"최근 대화창과 내가 닫은 대화창이 여기에 표시됩니다."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
new file mode 100644
index 0000000..827561e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(제목 없는 프로그램)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP 닫기"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"전체화면"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
new file mode 100644
index 0000000..6862aa1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Жабуу"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Жайып көрсөтүү"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Жөндөөлөр"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Эгер <xliff:g id="NAME">%s</xliff:g> колдонмосу бул функцияны пайдаланбасын десеңиз, жөндөөлөрдү ачып туруп, аны өчүрүп коюңуз."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Ойнотуу"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Тындыруу"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Кийинкисине өткөрүп жиберүү"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Мурункусуна өткөрүп жиберүү"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлчөмүн өзгөртүү"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Колдонмону кошумча экрандарда иштетүүгө болбойт."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Экранды бөлгүч"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Сол жактагы экранды толук экран режимине өткөрүү"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Сол жактагы экранды 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Сол жактагы экранды 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Сол жактагы экранды 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Оң жактагы экранды толук экран режимине өткөрүү"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Үстүнкү экранды толук экран режимине өткөрүү"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Үстүнкү экранды 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Үстүнкү экранды 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Үстүнкү экранды 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ылдыйкы экранды толук экран режимине өткөрүү"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> калкып чыкма билдирмелер жөндөөлөрү"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Кошумча меню"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Кайра топтомго кошуу"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосунан <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> жана дагы <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> колдонмодон <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Жогорку сол жакка жылдыруу"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдырыңыз"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү сол жакка жылдыруу"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдырыңыз"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> жөндөөлөрү"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
new file mode 100644
index 0000000..68376b1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Аталышы жок программа)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
new file mode 100644
index 0000000..2973414
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ປິດ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ຂະຫຍາຍ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ການຕັ້ງຄ່າ"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"ເມນູ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ແມ່ນເປັນການສະແດງຜົນຫຼາຍຢ່າງພ້ອມກັນ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"ຫາກທ່ານບໍ່ຕ້ອງການ <xliff:g id="NAME">%s</xliff:g> ໃຫ້ໃຊ້ຄຸນສົມບັດນີ້, ໃຫ້ແຕະເພື່ອເປີດການຕັ້ງຄ່າ ແລ້ວປິດມັນໄວ້."</string>
+ <string name="pip_play" msgid="3496151081459417097">"ຫຼິ້ນ"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ຢຸດຊົ່ວຄາວ"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ຂ້າມໄປລາຍການໜ້າ"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ຂ້າມໄປລາຍການກ່ອນນີ້"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ປ່ຽນຂະໜາດ"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ແອັບບໍ່ຮອງຮັບການເປີດໃນໜ້າຈໍທີສອງ."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"ຕົວຂັ້ນການແບ່ງໜ້າຈໍ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ເຕັມໜ້າຈໍຊ້າຍ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ຊ້າຍ 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ຊ້າຍ 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ຊ້າຍ 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ເຕັມໜ້າຈໍຂວາ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ເຕັມໜ້າຈໍເທິງສຸດ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ເທິງສຸດ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ເທິງສຸດ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ເທິງສຸດ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ເຕັມໜ້າຈໍລຸ່ມສຸດ"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"ການຕັ້ງຄ່າສຳລັບຟອງ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ລົ້ນ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ເພີ່ມກັບໄປຫາການວາງຊ້ອນກັນ"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ຈາກ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ຈາກ <xliff:g id="APP_NAME">%2$s</xliff:g> ແລະ ອີກ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ຍ້າຍຊ້າຍເທິງ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ຍ້າຍຂວາເທິງ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ຍ້າຍຂວາລຸ່ມ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ປິດຟອງໄວ້"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ການສົນທະນາໃໝ່ຈະປາກົດເປັນໄອຄອນ ຫຼື ຟອງແບບລອຍ. ແຕະເພື່ອເປີດຟອງ. ລາກເພື່ອຍ້າຍມັນ."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ຄວບຄຸມຟອງຕອນໃດກໍໄດ້"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ແຕະຈັດການ ເພື່ອປິດຟອງຈາກແອັບນີ້"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ບໍ່ມີຟອງຫຼ້າສຸດ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ຟອງຫຼ້າສຸດ ແລະ ຟອງທີ່ປິດໄປຈະປາກົດຢູ່ບ່ອນນີ້"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
new file mode 100644
index 0000000..d132256
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
new file mode 100644
index 0000000..007d069
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Uždaryti"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Išskleisti"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nustatymai"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> rodom. vaizdo vaizde"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Jei nenorite, kad „<xliff:g id="NAME">%s</xliff:g>“ naudotų šią funkciją, palietę atidarykite nustatymus ir išjunkite ją."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Leisti"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pristabdyti"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Praleisti ir eiti į kitą"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Praleisti ir eiti į ankstesnį"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Pakeisti dydį"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skaidyto ekrano daliklis"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kairysis ekranas viso ekrano režimu"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kairysis ekranas 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kairysis ekranas 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kairysis ekranas 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Dešinysis ekranas viso ekrano režimu"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Viršutinis ekranas viso ekrano režimu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Viršutinis ekranas 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Viršutinis ekranas 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Viršutinis ekranas 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Apatinis ekranas viso ekrano režimu"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ burbulų nustatymai"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Perpildymas"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Pridėti atgal į krūvą"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ iš „<xliff:g id="APP_NAME">%2$s</xliff:g>“"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ iš „<xliff:g id="APP_NAME">%2$s</xliff:g>“ ir dar <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Perkelti į viršų kairėje"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti į viršų dešinėje"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti į apačią kairėje"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti į apačią dešinėje"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Pokalbis naudojant burbulus"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulai. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bet kada valdyti burbulus"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Palieskite „Tvarkyti“, kad išjungtumėte burbulus šioje programoje"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nėra naujausių burbulų"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Naujausi ir atsisakyti burbulai bus rodomi čia"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
new file mode 100644
index 0000000..189f766
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa be pavadinimo)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Uždaryti PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
new file mode 100644
index 0000000..a7f9617
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Aizvērt"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Izvērst"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Iestatījumi"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Izvēlne"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ir attēlā attēlā"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ja nevēlaties lietotnē <xliff:g id="NAME">%s</xliff:g> izmantot šo funkciju, pieskarieties, lai atvērtu iestatījumus un izslēgtu funkciju."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Atskaņot"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Apturēt"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Pāriet uz nākamo"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pāriet uz iepriekšējo"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Mainīt lielumu"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ekrāna sadalītājs"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kreisā daļa pa visu ekrānu"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Pa kreisi 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pa kreisi 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Pa kreisi 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Labā daļa pa visu ekrānu"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Augšdaļa pa visu ekrānu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Augšdaļa 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Augšdaļa 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Augšdaļa 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Apakšdaļu pa visu ekrānu"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> burbuļu iestatījumi"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Pārpilde"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Pievienot atpakaļ kopai"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> no: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> no lietotnes “<xliff:g id="APP_NAME">%2$s</xliff:g>” un vēl <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Pārvietot augšpusē pa kreisi"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pārvietot augšpusē pa labi"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pārvietot apakšpusē pa kreisi"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pārvietot apakšpusē pa labi"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Nerādīt burbuli"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerādīt sarunu burbuļos"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Tērzēšana, izmantojot burbuļus"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Jaunas sarunas tiek rādītas kā peldošas ikonas vai burbuļi. Pieskarieties, lai atvērtu burbuli. Velciet, lai to pārvietotu."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Allaž pārvaldīt burbuļus"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Pieskarieties pogai “Pārvaldīt”, lai izslēgtu burbuļus no šīs lietotnes."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nav nesen aizvērtu burbuļu"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Šeit būs redzami nesen rādītie burbuļi un aizvērtie burbuļi"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
new file mode 100644
index 0000000..614104b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma bez nosaukuma)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Aizvērt PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
new file mode 100644
index 0000000..9edb7be
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Проширете"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Поставки"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Мени"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> е во слика во слика"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ако не сакате <xliff:g id="NAME">%s</xliff:g> да ја користи функцијава, допрете за да ги отворите поставките и да ја исклучите."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Пушти"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Паузирај"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Прескокни до следната"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Прескокни до претходната"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промени големина"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликацијата не поддржува стартување на други екрани."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Разделник на поделен екран"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Левиот на цел екран"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Левиот 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Левиот 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Левиот 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Десниот на цел екран"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Горниот на цел екран"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Горниот 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Горниот 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Горниот 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Долниот на цел екран"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Поставки за балончињата за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Прелевање"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Додајте назад во stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> од <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> од <xliff:g id="APP_NAME">%2$s</xliff:g> и уште <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Премести горе лево"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести долу лево"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести долу десно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отфрли балонче"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не прикажувај го разговорот во балончиња"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Разговор во балончиња"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори ќе се појавуваат како лебдечки икони или балончиња. Допрете за отворање на балончето. Повлечете за да го преместите."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контролирајте ги балончињата во секое време"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Допрете „Управувајте“ за да ги исклучите балончињата од апликацијава"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема неодамнешни балончиња"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
new file mode 100644
index 0000000..c279f79
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
new file mode 100644
index 0000000..dfa4b22
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"അവസാനിപ്പിക്കുക"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"വികസിപ്പിക്കുക"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ക്രമീകരണം"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"മെനു"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ചിത്രത്തിനുള്ളിൽ ചിത്രം രീതിയിലാണ്"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ഈ ഫീച്ചർ ഉപയോഗിക്കേണ്ടെങ്കിൽ, ടാപ്പ് ചെയ്ത് ക്രമീകരണം തുറന്ന് അത് ഓഫാക്കുക."</string>
+ <string name="pip_play" msgid="3496151081459417097">"പ്ലേ ചെയ്യുക"</string>
+ <string name="pip_pause" msgid="690688849510295232">"താൽക്കാലികമായി നിർത്തുക"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"രണ്ടാം ഡിസ്പ്ലേകളിൽ സമാരംഭിക്കുന്നതിനെ ആപ്പ് അനുവദിക്കുന്നില്ല."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"സ്പ്ലിറ്റ്-സ്ക്രീൻ ഡിവൈഡർ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ഇടത് പൂർണ്ണ സ്ക്രീൻ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ഇടത് 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ഇടത് 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ഇടത് 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"വലത് പൂർണ്ണ സ്ക്രീൻ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"മുകളിൽ പൂർണ്ണ സ്ക്രീൻ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"മുകളിൽ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"മുകളിൽ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"മുകളിൽ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"താഴെ പൂർണ്ണ സ്ക്രീൻ"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബബിളുകളുടെ ക്രമീകരണം"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ഓവർഫ്ലോ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"അടുക്കുകളിലേക്ക് തിരിച്ച് ചേർക്കുക"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>-ൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> കൂടുതലും"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ഈ ആപ്പിൽ നിന്നുള്ള ബബിളുകൾ ഓഫാക്കാൻ മാനേജ് ചെയ്യുക ടാപ്പ് ചെയ്യുക"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
new file mode 100644
index 0000000..7aaf79f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്ണ്ണ സ്ക്രീന്"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
new file mode 100644
index 0000000..071629f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Хаах"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Дэлгэх"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Тохиргоо"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Цэс"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Та <xliff:g id="NAME">%s</xliff:g>-д энэ онцлогийг ашиглуулахыг хүсэхгүй байвал тохиргоог нээгээд, үүнийг унтраана уу."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Тоглуулах"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Түр зогсоох"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Дараагийн медиад очих"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Өмнөх медиад очих"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Хэмжээг өөрчлөх"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Аппыг хоёрдогч дэлгэцэд эхлүүлэх боломжгүй."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"\"Дэлгэц хуваах\" хуваагч"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Зүүн талын бүтэн дэлгэц"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Зүүн 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Зүүн 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Зүүн 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Баруун талын бүтэн дэлгэц"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Дээд талын бүтэн дэлгэц"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Дээд 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Дээд 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Дээд 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Доод бүтэн дэлгэц"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н бөмбөлгүүдийн тохиргоо"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Халих"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Өрөлтөд буцааж нэмэх"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>-н <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g>-н <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> болон бусад <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Зүүн дээш зөөх"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Баруун дээш зөөх"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Зүүн доош зөөх"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Баруун доош зөөх"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Бөмбөлгийг хаах"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Харилцан яриаг бүү бөмбөлөг болго"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Бөмбөлөг ашиглан чатлаарай"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Шинэ харилцан яриа нь хөвөгч дүрс тэмдэг эсвэл бөмбөлөг хэлбэрээр харагддаг. Бөмбөлгийг нээхийн тулд товшино уу. Түүнийг зөөхийн тулд чирнэ үү."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Дурын үед бөмбөлгийг хянаарай"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Энэ аппын бөмбөлгүүдийг унтраахын тулд Удирдах дээр товшино уу"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Саяхны бөмбөлөг алга байна"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Саяхны бөмбөлгүүд болон үл хэрэгссэн бөмбөлгүүд энд харагдана"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
new file mode 100644
index 0000000..e4b0262
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
new file mode 100644
index 0000000..bd28756
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"बंद करा"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत करा"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग्ज"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"मेनू"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> चित्रामध्ये चित्र मध्ये आहे"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>ने हे वैशिष्ट्य वापरू नये असे तुम्हाला वाटत असल्यास, सेटिंग्ज उघडण्यासाठी टॅप करा आणि ते बंद करा."</string>
+ <string name="pip_play" msgid="3496151081459417097">"प्ले करा"</string>
+ <string name="pip_pause" msgid="690688849510295232">"थांबवा"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"डावलून पुढे जा"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"डावलून मागे जा"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदला"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अॅप कदाचित चालणार नाही."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"दुसऱ्या डिस्प्लेवर अॅप लाँच होणार नाही."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रीन विभाजक"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"डावी फुल स्क्रीन"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"डावी 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"डावी 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"डावी 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"उजवी फुल स्क्रीन"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"शीर्ष फुल स्क्रीन"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"शीर्ष 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"शीर्ष 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"शीर्ष 10"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"तळाशी फुल स्क्रीन"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> बबलसाठी सेटिंग्ज"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ओव्हरफ्लो"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"स्टॅकमध्ये परत जोडा"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> कडून <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> आणि आणखी <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> कडून <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"वर डावीकडे हलवा"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर उजवीकडे हलवा"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"तळाशी डावीकडे हलवा"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"तळाशी उजवीकडे हलवा"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> सेटिंग्ज"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल डिसमिस करा"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"संभाषणाला बबल करू नका"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल वापरून चॅट करा"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"नवीन संभाषणे फ्लोटिंग आयकन किंवा बबल म्हणून दिसतात. बबल उघडण्यासाठी टॅप करा. हे हलवण्यासाठी ड्रॅग करा."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"बबल कधीही नियंत्रित करा"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"या अॅपमधून बबल बंद करण्यासाठी व्यवस्थापित करा वर टॅप करा"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"अलीकडील कोणतेही बबल नाहीत"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
new file mode 100644
index 0000000..d9fc3b1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
new file mode 100644
index 0000000..267809f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Kembangkan"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Tetapan"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Jika anda tidak mahu <xliff:g id="NAME">%s</xliff:g> menggunakan ciri ini, ketik untuk membuka tetapan dan matikan ciri."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Main"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Jeda"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Langkau ke seterusnya"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Langkau ke sebelumnya"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah saiz"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Pembahagi skrin pisah"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrin penuh kiri"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kiri 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kiri 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Skrin penuh kanan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Skrin penuh atas"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Atas 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Atas 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Skrin penuh bawah"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Tetapan untuk gelembung <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Limpahan"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Tambah kembali pada tindanan"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> daripada <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> daripada <xliff:g id="APP_NAME">%2$s</xliff:g> dan <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> lagi"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Alihkan ke atas sebelah kiri"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bersembang menggunakan gelembung"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Perbualan baharu muncul sebagai ikon terapung atau gelembung. Ketik untuk membuka gelembung. Seret untuk mengalihkan gelembung tersebut."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kawal gelembung pada bila-bila masa"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Ketik Urus untuk mematikan gelembung daripada apl ini"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tiada gelembung terbaharu"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Gelembung baharu dan gelembung yang diketepikan akan dipaparkan di sini"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
new file mode 100644
index 0000000..9597a79
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
new file mode 100644
index 0000000..b913a6b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ပိတ်ရန်"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ချဲ့ရန်"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ဆက်တင်များ"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"မီနူး"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> သည် တစ်ခုပေါ် တစ်ခုထပ်၍ ဖွင့်ထားသည်"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> အား ဤဝန်ဆောင်မှုကို အသုံးမပြုစေလိုလျှင် ဆက်တင်ကိုဖွင့်ရန် တို့ပြီး ၎င်းဝန်ဆောင်မှုကို ပိတ်လိုက်ပါ။"</string>
+ <string name="pip_play" msgid="3496151081459417097">"ဖွင့်ရန်"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ခေတ္တရပ်ရန်"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"နောက်တစ်ခုသို့ ကျော်ရန်"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ယခင်တစ်ခုသို့ ပြန်သွားရန်"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"အရွယ်အစားပြောင်းရန်"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ဤအက်ပ်အနေဖြင့် ဖွင့်ရန်စနစ်ကို ဒုတိယဖန်သားပြင်မှ အသုံးပြုရန် ပံ့ပိုးမထားပါ။"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"မျက်နှာပြင်ခွဲခြမ်း ပိုင်းခြားပေးသည့်စနစ်"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ဘယ်ဘက် မျက်နှာပြင်အပြည့်"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ဘယ်ဘက်မျက်နှာပြင် ၇၀%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ဘယ်ဘက် မျက်နှာပြင် ၅၀%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ဘယ်ဘက် မျက်နှာပြင် ၃၀%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ညာဘက် မျက်နှာပြင်အပြည့်"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"အပေါ်ဘက် မျက်နှာပြင်အပြည့်"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"အပေါ်ဘက် မျက်နှာပြင် ၇၀%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"အပေါ်ဘက် မျက်နှာပြင် ၅၀%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"အပေါ်ဘက် မျက်နှာပြင် ၃၀%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"အောက်ခြေ မျက်နှာပြင်အပြည့်"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ပူဖောင်းကွက်အတွက် ဆက်တင်များ"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"အပိုများပြရန်"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ပူဖေါင်းတန်းသို့ ပြန်ထည့်ရန်"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> မှ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> နှင့် နောက်ထပ် <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ခုမှ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ညာဘက်ထိပ်သို့ ရွှေ့ပါ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ဘယ်အောက်ခြေသို့ ရွှေ့ရန်"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ဆက်တင်များ"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ပူဖောင်းကွက် ပယ်ရန်"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ပူဖောင်းကွက် သုံး၍ ချတ်လုပ်ခြင်း"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"စကားဝိုင်းအသစ်များကို မျောနေသည့် သင်္ကေတများ သို့မဟုတ် ပူဖောင်းကွက်များအဖြစ် မြင်ရပါမည်။ ပူဖောင်းကွက်ကိုဖွင့်ရန် တို့ပါ။ ရွှေ့ရန် ၎င်းကို ဖိဆွဲပါ။"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ပူဖောင်းကွက်ကို အချိန်မရွေး ထိန်းချုပ်ရန်"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ဤအက်ပ်မှနေ၍ ပူဖောင်းများကို ပိတ်ရန်အတွက် \'စီမံရန်\' ကို တို့ပါ"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"လတ်တလော ပူဖောင်းကွက်များ မရှိပါ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"လတ်တလော ပူဖောင်းကွက်များနှင့် ပိတ်လိုက်သော ပူဖောင်းကွက်များကို ဤနေရာတွင် မြင်ရပါမည်"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
new file mode 100644
index 0000000..6d4d6f0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ခေါင်းစဉ်မဲ့ အစီအစဉ်)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ကိုပိတ်ပါ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
new file mode 100644
index 0000000..cd601ea
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Lukk"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Vis"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Innstillinger"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meny"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> er i bilde-i-bilde"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Hvis du ikke vil at <xliff:g id="NAME">%s</xliff:g> skal bruke denne funksjonen, kan du trykke for å åpne innstillingene og slå den av."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Spill av"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Sett på pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Hopp til neste"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Hopp til forrige"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Endre størrelse"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke kjøres på sekundære skjermer."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skilleelement for delt skjerm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Utvid den venstre delen av skjermen til hele skjermen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Sett størrelsen på den venstre delen av skjermen til 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sett størrelsen på den venstre delen av skjermen til 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Sett størrelsen på den venstre delen av skjermen til 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Utvid den høyre delen av skjermen til hele skjermen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Utvid den øverste delen av skjermen til hele skjermen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Sett størrelsen på den øverste delen av skjermen til 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Sett størrelsen på den øverste delen av skjermen til 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Sett størrelsen på den øverste delen av skjermen til 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Utvid den nederste delen av skjermen til hele skjermen"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Innstillinger for <xliff:g id="APP_NAME">%1$s</xliff:g>-bobler"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflyt"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Legg tilbake i stabelen"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> flere"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Flytt til øverst til venstre"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til øverst til høyre"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til høyre"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat med bobler"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne bobler. Dra for å flytte dem."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrollér bobler når som helst"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Trykk på Administrer for å slå av bobler for denne appen"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nylige bobler og avviste bobler vises her"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
new file mode 100644
index 0000000..849ccda
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uten tittel)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
new file mode 100644
index 0000000..0c68bab
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"बन्द गर्नुहोस्"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत गर्नुहोस्"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"सेटिङहरू"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"मेनु"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> Picture-in-picture मा छ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले सुविधा प्रयोग नगरोस् भन्ने चाहनुहुन्छ भने ट्याप गरेर सेटिङहरू खोल्नुहोस् र यसलाई निष्क्रिय पार्नुहोस्।"</string>
+ <string name="pip_play" msgid="3496151081459417097">"प्ले गर्नुहोस्"</string>
+ <string name="pip_pause" msgid="690688849510295232">"पज गर्नुहोस्"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"अर्कोमा जानुहोस्"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"अघिल्लोमा जानुहोस्"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"बायाँ भाग ७०%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"बायाँ भाग ५०%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"बायाँ भाग ३०%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"दायाँ भाग फुल स्क्रिन"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"माथिल्लो भाग फुल स्क्रिन"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"माथिल्लो भाग ७०%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"माथिल्लो भाग ५०%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"माथिल्लो भाग ३०%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"तल्लो भाग फुल स्क्रिन"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> का बबलसम्बन्धी सेटिङहरू"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ओभरफ्लो देखाउनुहोस्"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"स्ट्याकमा फेरि थप्नुहोस्"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> को <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> का <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> र थप <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"शीर्ष भागको बायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा दायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"पुछारमा बायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"पुछारमा दायाँतिर सार्नुहोस्"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाइयोस्"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबलहरू प्रयोग गरी कुराकानी गर्नुहोस्"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"नयाँ वार्तालापहरू तैरने आइकन वा बबलका रूपमा देखिन्छन्। बबल खोल्न ट्याप गर्नुहोस्। बबल सार्न सो बबललाई ड्र्याग गर्नुहोस्।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"जुनसुकै बेला बबलहरू नियन्त्रण गर्नुहोस्"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"यो एपबाट आएका बबलहरू अफ गर्न \"व्यवस्थापन गर्नुहोस्\" बटनमा ट्याप गर्नुहोस्"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हालैका बबलहरू छैनन्"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हालैका बबल र खारेज गरिएका बबलहरू यहाँ देखिने छन्"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
new file mode 100644
index 0000000..fc0221e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
new file mode 100644
index 0000000..2ead586
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Sluiten"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Uitvouwen"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en schakel je de functie uit."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Onderbreken"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Doorgaan naar volgende"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Teruggaan naar vorige"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Formaat aanpassen"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App kan niet op secundaire displays worden gestart."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Scheiding voor gesplitst scherm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Linkerscherm op volledig scherm"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Linkerscherm 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Linkerscherm 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Linkerscherm 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Rechterscherm op volledig scherm"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Bovenste scherm op volledig scherm"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Bovenste scherm 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellingen voor <xliff:g id="APP_NAME">%1$s</xliff:g>-bubbels"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overloop"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Weer toevoegen aan stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g> en nog <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Naar linksboven verplaatsen"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
new file mode 100644
index 0000000..281eaf0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
new file mode 100644
index 0000000..2b27c99
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ବଢ଼ାନ୍ତୁ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ସେଟିଂସ୍"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"ମେନୁ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"ଛବି-ଭିତରେ-ଛବି\"ରେ ଅଛି"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"ଏହି ବୈଶିଷ୍ଟ୍ୟ <xliff:g id="NAME">%s</xliff:g> ବ୍ୟବହାର ନକରିବାକୁ ଯଦି ଆପଣ ଚାହାଁନ୍ତି, ସେଟିଙ୍ଗ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ ଏବଂ ଏହା ଅଫ୍ କରିଦିଅନ୍ତୁ।"</string>
+ <string name="pip_play" msgid="3496151081459417097">"ପ୍ଲେ କରନ୍ତୁ"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ପଜ୍ କରନ୍ତୁ"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ରିସାଇଜ୍ କରନ୍ତୁ"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍ ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍ କାମ ନକରିପାରେ।"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍ ଲଞ୍ଚ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରୀନ ବିଭାଜକ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ବାମ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ବାମ ପଟକୁ 70% କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ବାମ ପଟକୁ 50% କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ବାମ ପଟେ 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ଡାହାଣ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ଉପର ଆଡ଼କୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ଉପର ଆଡ଼କୁ 70% କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ଉପର ଆଡ଼କୁ 50% କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ଉପର ଆଡ଼କୁ 30% କରନ୍ତୁ"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ତଳ ଅଂଶର ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବବଲ୍ଗୁଡ଼ିକ ପାଇଁ ସେଟିଂସ୍"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ଓଭରଫ୍ଲୋ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ଷ୍ଟାକରେ ପୁଣି ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>ରୁ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ଏବଂ ଅଧିକ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>ଟିରୁ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ଉପର ବାମକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ସେଟିଂସ୍"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଫ୍ଲୋଟିଂ ଆଇକନ୍ କିମ୍ବା ବବଲ୍ ଭାବେ ଦେଖାଯିବ। ବବଲ୍ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଏହାକୁ ମୁଭ୍ କରିବାକୁ ଟାଣନ୍ତୁ।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ଯେ କୌଣସି ସମୟରେ ବବଲଗୁଡ଼ିକ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ଏହି ଆପର ବବଲଗୁଡ଼ିକ ବନ୍ଦ କରିବା ପାଇଁ \'ପରିଚାଳନା କରନ୍ତୁ\' ବଟନରେ ଟାପ୍ କରନ୍ତୁ"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ବର୍ତ୍ତମାନ କୌଣସି ବବଲ୍ ନାହିଁ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ବର୍ତ୍ତମାନର ଏବଂ ଖାରଜ କରାଯାଇଥିବା ବବଲଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
new file mode 100644
index 0000000..b57dd93
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(କୌଣସି ଟାଇଟଲ୍ ପ୍ରୋଗ୍ରାମ୍ ନାହିଁ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
new file mode 100644
index 0000000..b9cb65e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ਵਿਸਤਾਰ ਕਰੋ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ਸੈਟਿੰਗਾਂ"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"ਮੀਨੂ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ਤਸਵੀਰ-ਅੰਦਰ-ਤਸਵੀਰ ਵਿੱਚ ਹੈ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"ਜੇਕਰ ਤੁਸੀਂ ਨਹੀਂ ਚਾਹੁੰਦੇ ਕਿ <xliff:g id="NAME">%s</xliff:g> ਐਪ ਇਸ ਵਿਸ਼ੇਸ਼ਤਾ ਦੀ ਵਰਤੋਂ ਕਰੇ, ਤਾਂ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਇਸਨੂੰ ਬੰਦ ਕਰੋ।"</string>
+ <string name="pip_play" msgid="3496151081459417097">"ਚਲਾਓ"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ਵਿਰਾਮ ਦਿਓ"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ਅਗਲੇ \'ਤੇ ਜਾਓ"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ਪਿਛਲੇ \'ਤੇ ਜਾਓ"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ਆਕਾਰ ਬਦਲੋ"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇਆਂ \'ਤੇ ਲਾਂਚ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਡਿਵਾਈਡਰ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ਖੱਬੇ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ਖੱਬੇ 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ਖੱਬੇ 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ਖੱਬੇ 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ਸੱਜੇ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ਉੱਪਰ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ਉੱਪਰ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ਉੱਪਰ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ਉੱਪਰ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ਹੇਠਾਂ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਬਬਲ ਲਈ ਸੈਟਿੰਗਾਂ"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ਓਵਰਫ਼ਲੋ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ਸਟੈਕ ਵਿੱਚ ਵਾਪਸ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਤੋਂ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਅਤੇ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ਹੋਰਾਂ ਤੋਂ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ਸੈਟਿੰਗਾਂ"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ਬਬਲ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ਨਵੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਫਲੋਟਿੰਗ ਪ੍ਰਤੀਕਾਂ ਜਾਂ ਬਬਲ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ। ਬਬਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਇਸਨੂੰ ਲਿਜਾਣ ਲਈ ਘਸੀਟੋ।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ਬਬਲ ਨੂੰ ਕਿਸੇ ਵੇਲੇ ਵੀ ਕੰਟਰੋਲ ਕਰੋ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ਇਸ ਐਪ \'ਤੇ ਬਬਲ ਬੰਦ ਕਰਨ ਲਈ \'ਪ੍ਰਬੰਧਨ ਕਰੋ\' \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ਕੋਈ ਹਾਲੀਆ ਬਬਲ ਨਹੀਂ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ਹਾਲੀਆ ਬਬਲ ਅਤੇ ਖਾਰਜ ਕੀਤੇ ਬਬਲ ਇੱਥੇ ਦਿਸਣਗੇ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
new file mode 100644
index 0000000..028a0a0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
new file mode 100644
index 0000000..bc473b5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zamknij"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Rozwiń"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ustawienia"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"Aplikacja <xliff:g id="NAME">%s</xliff:g> działa w trybie obraz w obrazie"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Jeśli nie chcesz, by aplikacja <xliff:g id="NAME">%s</xliff:g> korzystała z tej funkcji, otwórz ustawienia i wyłącz ją."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Odtwórz"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Wstrzymaj"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Dalej"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Wstecz"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmień rozmiar"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacja nie obsługuje uruchamiania na dodatkowych ekranach."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Linia dzielenia ekranu"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lewa część ekranu na pełnym ekranie"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% lewej części ekranu"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% lewej części ekranu"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% lewej części ekranu"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Prawa część ekranu na pełnym ekranie"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Górna część ekranu na pełnym ekranie"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% górnej części ekranu"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% górnej części ekranu"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% górnej części ekranu"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolna część ekranu na pełnym ekranie"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ustawienia dymków aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Przepełnienie"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj ponownie do stosu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g> i jeszcze <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Przenieś w lewy górny róg"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Przenieś w prawy górny róg"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Przenieś w lewy dolny róg"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Przenieś w prawy dolny róg"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyświetlaj rozmowy jako dymka"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Czatuj, korzystając z dymków"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Zarządzaj dymkami w dowolnym momencie"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Kliknij Zarządzaj, aby wyłączyć dymki z tej aplikacji"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Brak ostatnich dymków"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tutaj będą pojawiać się ostatnie i odrzucone dymki"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
new file mode 100644
index 0000000..4522ea1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez tytułu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zamknij PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..612377d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Se você não quer que o app <xliff:g id="NAME">%s</xliff:g> use este recurso, toque para abrir as configurações e desativá-lo."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de tela"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lado esquerdo em tela cheia"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Esquerda a 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Esquerda a 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Lado direito em tela cheia"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Parte superior em tela cheia"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Parte superior a 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Parte superior a 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Parte inferior em tela cheia"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> mais <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover para canto superior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controle os balões a qualquer momento"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toque em \"Gerenciar\" para desativar os balões desse app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
new file mode 100644
index 0000000..951babe
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..bbd233e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Definições"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"A app <xliff:g id="NAME">%s</xliff:g> está no modo de ecrã no ecrã"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Se não pretende que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Mudar para o anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A app não é compatível com o início em ecrãs secundários."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor do ecrã dividido"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ecrã esquerdo inteiro"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% no ecrã esquerdo"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% no ecrã esquerdo"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% no ecrã esquerdo"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ecrã direito inteiro"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ecrã superior inteiro"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% no ecrã superior"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% no ecrã superior"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% no ecrã superior"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ecrã inferior inteiro"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Definições dos balões da app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu adicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adicionar novamente à pilha"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> do <xliff:g id="APP_NAME">%2$s</xliff:g> e mais<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>."</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover p/ parte sup. esquerda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover parte superior direita"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover parte inferior direita"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balão"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não apresentar a conversa em balões"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse no chat através de balões"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"As novas conversas aparecem como ícones flutuantes ou balões. Toque para abrir o balão. Arraste para o mover."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controle os balões em qualquer altura"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toque em Gerir para desativar os balões desta app."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e ignorados vão aparecer aqui."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
new file mode 100644
index 0000000..05d976c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sem título do programa)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
new file mode 100644
index 0000000..612377d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Se você não quer que o app <xliff:g id="NAME">%s</xliff:g> use este recurso, toque para abrir as configurações e desativá-lo."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de tela"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lado esquerdo em tela cheia"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Esquerda a 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Esquerda a 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Lado direito em tela cheia"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Parte superior em tela cheia"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Parte superior a 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Parte superior a 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Parte inferior em tela cheia"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> mais <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover para canto superior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controle os balões a qualquer momento"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toque em \"Gerenciar\" para desativar os balões desse app"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
new file mode 100644
index 0000000..951babe
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
new file mode 100644
index 0000000..a31b8a1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Închideți"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Redați"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Întrerupeți"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplicația nu acceptă lansare pe ecrane secundare."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Separator pentru ecranul împărțit"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Partea stângă pe ecran complet"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Partea stângă: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Partea stângă: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Partea stângă: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Partea dreaptă pe ecran complet"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Partea de sus pe ecran complet"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Partea de sus: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Partea de sus: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Partea de sus: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setări pentru baloanele <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Suplimentar"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adăugați înapoi în stivă"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mutați în stânga sus"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mutați în dreapta sus"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mutați în stânga jos"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mutați în dreapta jos"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închideți balonul"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișați conversația în balon"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat cu baloane"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atingeți pentru a deschide balonul. Trageți pentru a-l muta."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlați oricând baloanele"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Atingeți Gestionați pentru a dezactiva baloanele din această aplicație"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
new file mode 100644
index 0000000..0dc28f0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Închideți PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
new file mode 100644
index 0000000..849ab28
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Закрыть"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Развернуть"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> находится в режиме \"Картинка в картинке\""</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Чтобы отключить эту функцию для приложения \"<xliff:g id="NAME">%s</xliff:g>\", перейдите в настройки."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Воспроизвести"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Приостановить"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти к следующему"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти к предыдущему"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Изменить размер"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Приложение не поддерживает запуск на дополнительных экранах"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Разделитель экрана"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Левый во весь экран"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Левый на 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Левый на 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Левый на 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Правый во весь экран"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Верхний во весь экран"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Верхний на 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Верхний на 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Верхний на 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Нижний во весь экран"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Настройки всплывающих чатов от приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Дополнительное меню"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Добавить обратно в стек"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> от приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" и ещё <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перенести в левый верхний угол"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перенести в правый верхний угол"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перенести в левый нижний угол"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перенести в правый нижний угол"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Скрыть всплывающий чат"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показывать всплывающий чат для разговора"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Всплывающие чаты"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новые разговоры будут появляться в виде плавающих значков, или всплывающих чатов. Чтобы открыть чат, нажмите на него, а чтобы переместить – перетащите."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Всплывающие чаты"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Чтобы отключить всплывающие чаты из этого приложения, нажмите \"Настроить\"."</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нет недавних всплывающих чатов"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Здесь будут появляться недавние и скрытые всплывающие чаты."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
new file mode 100644
index 0000000..238d2d5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
new file mode 100644
index 0000000..35c8388
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"වසන්න"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"දිග හරින්න"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"සැකසීම්"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"මෙනුව"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> පින්තූරය-තුළ-පින්තූරය තුළ වේ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"ඔබට <xliff:g id="NAME">%s</xliff:g> මෙම විශේෂාංගය භාවිත කිරීමට අවශ්ය නැති නම්, සැකසීම් විවෘත කිරීමට තට්ටු කර එය ක්රියාවිරහිත කරන්න."</string>
+ <string name="pip_play" msgid="3496151081459417097">"ධාවනය කරන්න"</string>
+ <string name="pip_pause" msgid="690688849510295232">"විරාම කරන්න"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ඊළඟ එකට පනින්න"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"පෙර එකට පනින්න"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ප්රතිප්රමාණ කරන්න"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්රියා නොකළ හැකිය."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"යෙදුම ද්විතීයික සංදර්ශක මත දියත් කිරීම සඳහා සහාය නොදක්වයි."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"බෙදුම්-තිර වෙන්කරණය"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"වම් පූර්ණ තිරය"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"වම් 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"වම් 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"වම් 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"දකුණු පූර්ණ තිරය"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ඉහළම පූර්ණ තිරය"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ඉහළම 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ඉහළම 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ඉහළම 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"පහළ පූර්ණ තිරය"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> බුබුළු සඳහා සැකසීම්"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"පිටාර යාම"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"අට්ටිය වෙත ආපසු එක් කරන්න"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> වෙතින් <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> වෙතින් <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සහ තවත් <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ක්"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ඉහළ වමට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ඉහළ දකුණට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"පහළ වමට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"පහළ දකුණට ගෙන යන්න"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"බුබුලු ඉවත ලන්න"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"සංවාදය බුබුලු නොදමන්න"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"නව සංවාද පාවෙන අයිකන හෝ බුබුලු ලෙස දිස් වේ. බුබුල විවෘත කිරීමට තට්ටු කරන්න. එය ගෙන යාමට අදින්න."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ඕනෑම වේලාවක බුබුලු පාලනය කරන්න"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"මෙම යෙදුමෙන් බුබුලු ක්රියාවිරහිත කිරීමට කළමනාකරණය කරන්න තට්ටු කරන්න"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"මෑත බුබුලු නැත"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"මෑත බුබුලු සහ ඉවත ලූ බුබුලු මෙහි දිස් වනු ඇත"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
new file mode 100644
index 0000000..4d8d97f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
new file mode 100644
index 0000000..b358894
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zavrieť"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Rozbaliť"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nastavenia"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Ponuka"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je v režime obraz v obraze"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ak nechcete, aby aplikácia <xliff:g id="NAME">%s</xliff:g> používala túto funkciu, klepnutím otvorte nastavenia a vypnite ju."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Prehrať"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pozastaviť"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskočiť na ďalšie"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskočiť na predchádzajúce"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmeniť veľkosť"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikácia nepodporuje spúšťanie na sekundárnych obrazovkách."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Rozdeľovač obrazovky"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ľavá – na celú obrazovku"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ľavá – 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ľavá – 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ľavá – 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pravá– na celú obrazovku"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Horná – na celú obrazovku"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Horná – 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Horná – 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Horná – 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolná – na celú obrazovku"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Nastavenia bublín aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Rozšírená ponuka"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Pridať späť do zásobníka"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g> a ďalšie (<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>)"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Presunúť doľava nahor"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Presunúť doprava nahor"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Presunúť doľava nadol"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Presunúť doprava nadol"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavrieť bublinu"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovať konverzáciu ako bublinu"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Čet pomocou bublín"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzácie sa zobrazujú ako plávajúce ikony či bubliny. Bublinu otvoríte klepnutím. Premiestnite ju presunutím."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Nastavenie bublín môžete kedykoľvek zmeniť"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bubliny pre túto aplikáciu môžete vypnúť klepnutím na Spravovať"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žiadne nedávne bubliny"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tu sa budú zobrazovať nedávne a zavreté bubliny"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
new file mode 100644
index 0000000..1f9ee16
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez názvu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zavrieť režim PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
new file mode 100644
index 0000000..d3b2c81
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Zapri"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Razširi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nastavitve"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je v načinu slika v sliki"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Če ne želite, da aplikacija <xliff:g id="NAME">%s</xliff:g> uporablja to funkcijo, se dotaknite, da odprete nastavitve, in funkcijo izklopite."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Predvajaj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Začasno ustavi"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na naslednjega"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prejšnjega"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Spremeni velikost"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podpira zagona na sekundarnih zaslonih."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdelilnik zaslonov"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levi v celozaslonski način"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Levi 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desni v celozaslonski način"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Zgornji v celozaslonski način"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Zgornji 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Zgornji 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Zgornji 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Spodnji v celozaslonski način"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Nastavitve za oblačke aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Prelivanje"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj nazaj v sklad"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> in toliko drugih: <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premakni zgoraj levo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premakni zgoraj desno"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premakni spodaj levo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaček"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblačku"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Klepet z oblački"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi pogovori so prikazani kot lebdeče ikone ali oblački. Če želite odpreti oblaček, se ga dotaknite. Če ga želite premakniti, ga povlecite."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Upravljanje oblačkov"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dotaknite se »Upravljanje«, da izklopite oblačke iz te aplikacije"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ni nedavnih oblačkov"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tukaj bodo prikazani tako nedavni kot tudi opuščeni oblački"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
new file mode 100644
index 0000000..88639ee
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program brez naslova)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zapri način PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
new file mode 100644
index 0000000..61b6c36
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Mbyll"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Zgjero"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Cilësimet"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menyja"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Nëse nuk dëshiron që <xliff:g id="NAME">%s</xliff:g> ta përdorë këtë funksion, trokit për të hapur cilësimet dhe për ta çaktivizuar."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Luaj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Ndërprit"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Kalo te tjetra"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Kalo tek e mëparshmja"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ndrysho përmasat"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacioni nuk mbështet nisjen në ekrane dytësore."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ndarësi i ekranit të ndarë"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ekrani i plotë majtas"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Majtas 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Majtas 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Majtas 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ekrani i plotë djathtas"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ekrani i plotë lart"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Lart 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Lart 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Lart 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ekrani i plotë poshtë"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Cilësimet për flluskat e <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Tejkalo"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Shto përsëri te stiva"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> nga <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> nga <xliff:g id="APP_NAME">%2$s</xliff:g> dhe <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> të tjera"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Zhvendos lart majtas"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Lëviz lart djathtas"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtë majtas"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Lëvize poshtë djathtas"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskën"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedën në flluskë"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bisedo duke përdorur flluskat"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Bisedat e reja shfaqen si ikona pluskuese ose flluska. Trokit për të hapur flluskën. Zvarrit për ta zhvendosur."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrollo flluskat në çdo moment"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Trokit \"Menaxho\" për të çaktivizuar flluskat nga ky aplikacion"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nuk ka flluska të fundit"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Flluskat e fundit dhe flluskat e hequra do të shfaqen këtu"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
new file mode 100644
index 0000000..e81275e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program pa titull)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
new file mode 100644
index 0000000..32de8a3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Прошири"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Подешавања"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Мени"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> је слика у слици"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ако не желите да <xliff:g id="NAME">%s</xliff:g> користи ову функцију, додирните да бисте отворили подешавања и искључили је."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Пусти"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Паузирај"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Пређи на следеће"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Пређи на претходно"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промените величину"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликација не подржава покретање на секундарним екранима."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Разделник подељеног екрана"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Режим целог екрана за леви екран"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Леви екран 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Леви екран 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Леви екран 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Режим целог екрана за доњи екран"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Режим целог екрана за горњи екран"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Горњи екран 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Горњи екран 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Горњи екран 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Режим целог екрана за доњи екран"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Подешавања за <xliff:g id="APP_NAME">%1$s</xliff:g> облачиће"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Преклапање"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Додај поново у групу"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из апликације <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из апликације <xliff:g id="APP_NAME">%2$s</xliff:g> и још <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Премести горе лево"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ћаскајте у облачићима"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контролишите облачиће у било ком тренутку"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Додирните Управљајте да бисте искључили облачиће из ове апликације"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема недавних облачића"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Овде се приказују недавни и одбачени облачићи"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
new file mode 100644
index 0000000..a06da11
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
new file mode 100644
index 0000000..2ec7944
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Stäng"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Utöka"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Inställningar"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Meny"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Om du inte vill att den här funktionen används i <xliff:g id="NAME">%s</xliff:g> öppnar du inställningarna genom att trycka. Sedan inaktiverar du funktionen."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Spela upp"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausa"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Hoppa till nästa"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Hoppa till föregående"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ändra storlek"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan inte köras på en sekundär skärm."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Avdelare för delad skärm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Helskärm på vänster skärm"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vänster 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vänster 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vänster 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Helskärm på höger skärm"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Helskärm på övre skärm"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Övre 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Övre 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Övre 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Helskärm på nedre skärm"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Inställningar för <xliff:g id="APP_NAME">%1$s</xliff:g>-bubblor"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Fler menyalternativ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Lägg tillbaka på stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> från <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> från <xliff:g id="APP_NAME">%2$s</xliff:g> och <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> fler"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Flytta högst upp till vänster"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta högst upp till höger"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta längst ned till vänster"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta längst ned till höger"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Stäng bubbla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta med bubblor"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nya konversationer visas som flytande ikoner, så kallade bubblor. Tryck på bubblan om du vill öppna den. Dra den om du vill flytta den."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Styr bubblor när som helst"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tryck på Hantera för att stänga av bubblor från den här appen"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Inga nya bubblor"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"De senaste bubblorna och ignorerade bubblor visas här"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
new file mode 100644
index 0000000..da0b121
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Namnlöst program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Stäng PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
new file mode 100644
index 0000000..5815d5e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Funga"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Panua"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Mipangilio"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> iko katika hali ya picha ndani ya picha nyingine"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> itumie kipengele hiki, gusa ili ufungue mipangilio na uizime."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Cheza"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Sitisha"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Ruka ufikie inayofuata"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Ruka ufikie iliyotangulia"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Badilisha ukubwa"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programu hii haiwezi kufunguliwa kwenye madirisha mengine."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Kitenganishi cha skrini inayogawanywa"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrini nzima ya kushoto"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kushoto 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kushoto 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kushoto 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Skrini nzima ya kulia"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Skrini nzima ya juu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Juu 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Juu 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Juu 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Skrini nzima ya chini"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Mipangilio ya viputo vya <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Vipengee vya ziada"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Rejesha kwenye rafu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kutoka kwa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kutoka kwa <xliff:g id="APP_NAME">%2$s</xliff:g> na nyingine<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Sogeza juu kushoto"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Piga gumzo ukitumia viputo"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Mazungumzo mapya huonekena kama aikoni au viputo vinavyoelea. Gusa ili ufungue kiputo. Buruta ili ukisogeze."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Dhibiti viputo wakati wowote"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Gusa Dhibiti ili uzime viputo kwenye programu hii"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hakuna viputo vya hivi majuzi"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viputo vya hivi karibuni na vile vilivyoondolewa vitaonekana hapa"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
new file mode 100644
index 0000000..9619523
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programu isiyo na jina)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Funga PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
new file mode 100644
index 0000000..81066ff
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"மூடு"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"விரி"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"அமைப்புகள்"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"மெனு"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> தற்போது பிக்ச்சர்-இன்-பிக்ச்சரில் உள்ளது"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> இந்த அம்சத்தைப் பயன்படுத்த வேண்டாம் என நினைத்தால் இங்கு தட்டி அமைப்புகளைத் திறந்து இதை முடக்கவும்."</string>
+ <string name="pip_play" msgid="3496151081459417097">"இயக்கு"</string>
+ <string name="pip_pause" msgid="690688849510295232">"இடைநிறுத்து"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"அடுத்ததற்குச் செல்"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"முந்தையதற்குச் செல்"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"அளவு மாற்று"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"இரண்டாம்நிலைத் திரைகளில் பயன்பாட்டைத் தொடங்க முடியாது."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"திரையைப் பிரிக்கும் பிரிப்பான்"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"இடது புறம் முழுத் திரை"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"இடது புறம் 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"இடது புறம் 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"இடது புறம் 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"வலது புறம் முழுத் திரை"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"மேற்புறம் முழுத் திரை"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"மேலே 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"மேலே 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"மேலே 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"கீழ்ப்புறம் முழுத் திரை"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> குமிழ்களுக்கான அமைப்புகள்"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ஓவர்ஃப்லோ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"மீண்டும் ஸ்டேக்கில் சேர்க்கவும்"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> இலிருந்து <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> மற்றும் மேலும் <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ஆப்ஸிலிருந்து வந்துள்ள அறிவிப்பு: <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"மேலே இடப்புறமாக நகர்த்து"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"மேலே வலப்புறமாக நகர்த்து"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"கீழே இடப்புறமாக நகர்த்து"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"கீழே வலதுபுறமாக நகர்த்து"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> அமைப்புகள்"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"குமிழை அகற்று"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"உரையாடலைக் குமிழாக்காதே"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"குமிழ்களைப் பயன்படுத்தி அரட்டையடியுங்கள்"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"புதிய உரையாடல்கள் மிதக்கும் ஐகான்களாகவோ குமிழ்களாகவோ தோன்றும். குமிழைத் திறக்க தட்டவும். நகர்த்த இழுக்கவும்."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"குமிழ்களை எப்போது வேண்டுமானாலும் கட்டுப்படுத்தலாம்"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"இந்த ஆப்ஸிலிருந்து வரும் குமிழ்களை முடக்க, நிர்வகி என்பதைத் தட்டவும்"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"சமீபத்திய குமிழ்கள் இல்லை"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"சமீபத்திய குமிழ்களும் நிராகரிக்கப்பட்ட குமிழ்களும் இங்கே தோன்றும்"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
new file mode 100644
index 0000000..f246069
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
new file mode 100644
index 0000000..0d166d8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"మూసివేయి"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"విస్తరింపజేయి"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"సెట్టింగ్లు"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"మెనూ"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> చిత్రంలో చిత్రం రూపంలో ఉంది"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ఈ లక్షణాన్ని ఉపయోగించకూడదు అని మీరు అనుకుంటే, సెట్టింగ్లను తెరవడానికి ట్యాప్ చేసి, దీన్ని ఆఫ్ చేయండి."</string>
+ <string name="pip_play" msgid="3496151081459417097">"ప్లే చేయి"</string>
+ <string name="pip_pause" msgid="690688849510295232">"పాజ్ చేయి"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"పరిమాణం మార్చు"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ఎడమవైపు 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ఎడమవైపు 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ఎడమవైపు 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ఎగువ 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ఎగువ 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ఎగువ 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ పూర్తి స్క్రీన్"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> బబుల్స్ సెట్టింగ్లు"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ఓవర్ఫ్లో"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"స్ట్యాక్కు తిరిగి జోడించండి"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> మరియు మరో <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ఎగువ ఎడమవైపునకు జరుపు"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ఎగువ కుడివైపునకు జరుపు"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"దిగువ ఎడమవైపునకు తరలించు"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"దిగవు కుడివైపునకు జరుపు"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్లు"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"బబుల్ను విస్మరించు"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"సంభాషణను బబుల్ చేయవద్దు"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"బబుల్స్ను ఉపయోగించి చాట్ చేయండి"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"కొత్త సంభాషణలు తేలియాడే చిహ్నాలుగా లేదా బబుల్స్ లాగా కనిపిస్తాయి. బబుల్ని తెరవడానికి నొక్కండి. తరలించడానికి లాగండి."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"బబుల్స్ను ఎప్పుడైనా నియంత్రించండి"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ఈ యాప్ నుండి వచ్చే బబుల్స్ను ఆఫ్ చేయడానికి మేనేజ్ బటన్ను ట్యాప్ చేయండి"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ఇటీవలి బబుల్స్ ఏవీ లేవు"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ఇటీవలి బబుల్స్ మరియు తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
new file mode 100644
index 0000000..f3019945
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIPని మూసివేయి"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"పూర్తి స్క్రీన్"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
new file mode 100644
index 0000000..442c3ca
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"ปิด"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ขยาย"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"การตั้งค่า"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"เมนู"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"หากคุณไม่ต้องการให้ <xliff:g id="NAME">%s</xliff:g> ใช้ฟีเจอร์นี้ ให้แตะเพื่อเปิดการตั้งค่าแล้วปิดฟีเจอร์"</string>
+ <string name="pip_play" msgid="3496151081459417097">"เล่น"</string>
+ <string name="pip_pause" msgid="690688849510295232">"หยุดชั่วคราว"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"ข้ามไปรายการถัดไป"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"ข้ามไปรายการก่อนหน้า"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ปรับขนาด"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"แอปไม่รองรับการเรียกใช้ในจอแสดงผลรอง"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"เส้นแบ่งหน้าจอ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"เต็มหน้าจอทางซ้าย"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ซ้าย 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ซ้าย 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ซ้าย 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"เต็มหน้าจอทางขวา"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"เต็มหน้าจอด้านบน"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ด้านบน 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ด้านบน 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ด้านบน 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"เต็มหน้าจอด้านล่าง"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"การตั้งค่าบับเบิล <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"รายการเพิ่มเติม"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"เพิ่มกลับไปที่สแต็ก"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> จาก <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> จาก <xliff:g id="APP_NAME">%2$s</xliff:g> และอีก <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> รายการ"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ย้ายไปด้านซ้ายบน"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ย้ายไปด้านขวาบน"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ย้ายไปด้านซ้ายล่าง"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขาวล่าง"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ปิดบับเบิล"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"แชทโดยใช้บับเบิล"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"การสนทนาใหม่ๆ จะปรากฏเป็นไอคอนแบบลอยหรือบับเบิล แตะเพื่อเปิดบับเบิล ลากเพื่อย้ายที่"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ควบคุมบับเบิลได้ทุกเมื่อ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"แตะ \"จัดการ\" เพื่อปิดบับเบิลจากแอปนี้"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ไม่มีบับเบิลเมื่อเร็วๆ นี้"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"บับเบิลที่แสดงและที่ปิดไปเมื่อเร็วๆ นี้จะปรากฏที่นี่"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
new file mode 100644
index 0000000..cd1b612
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ไม่มีชื่อรายการ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
new file mode 100644
index 0000000..1615aaef
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Isara"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Palawakin"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Mga Setting"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Kung ayaw mong magamit ni <xliff:g id="NAME">%s</xliff:g> ang feature na ito, i-tap upang buksan ang mga setting at i-off ito."</string>
+ <string name="pip_play" msgid="3496151081459417097">"I-play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"I-pause"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Lumaktaw sa susunod"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Lumaktaw sa nakaraan"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"I-resize"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Hindi sinusuportahan ng app ang paglulunsad sa mga pangalawang display."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divider ng split-screen"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"I-full screen ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Gawing 70% ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Gawing 50% ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Gawing 30% ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"I-full screen ang nasa kanan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"I-full screen ang nasa itaas"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gawing 70% ang nasa itaas"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gawing 50% ang nasa itaas"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gawing 30% ang nasa itaas"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"I-full screen ang nasa ibaba"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Mga setting para sa mga bubble ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Idagdag ulit sa stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> mula sa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> mula sa <xliff:g id="APP_NAME">%2$s</xliff:g> at <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> pa"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Ilipat sa kaliwa sa itaas"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ilipat sa kanan sa itaas"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ilipat sa kaliwa sa ibaba"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Mag-chat gamit ang bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Lumalabas bilang mga nakalutang na icon o bubble ang mga bagong pag-uusap. I-tap para buksan ang bubble. I-drag para ilipat ito."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolin ang mga bubble anumang oras"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"I-tap ang Pamahalaan para i-off ang mga bubble mula sa app na ito"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Walang kamakailang bubble"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Lalabas dito ang mga kamakailang bubble at na-dismiss na bubble"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
new file mode 100644
index 0000000..d5ebb9e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Walang pamagat na programa)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Isara ang PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
new file mode 100644
index 0000000..3b987e5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Kapat"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Genişlet"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>, pencere içinde pencere özelliğini kullanıyor"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> uygulamasının bu özelliği kullanmasını istemiyorsanız dokunarak ayarları açın ve söz konusu özelliği kapatın."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Oynat"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Duraklat"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Sonrakine atla"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Öncekine atla"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Yeniden boyutlandır"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uygulama ikincil ekranlarda başlatılmayı desteklemiyor."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcı"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Solda tam ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Solda %70"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Solda %50"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Solda %30"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Sağda tam ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Üstte tam ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Üstte %70"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Üstte %50"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Üstte %30"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Altta tam ekran"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> baloncukları için ayarlar"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Taşma"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yığına geri ekle"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> uygulamasından <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> uygulamasından <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ve diğer <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Sol üste taşı"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sağ üste taşı"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taşı"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sağ alta taşı"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baloncuğu kapat"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Görüşmeyi baloncuk olarak görüntüleme"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Baloncukları kullanarak sohbet edin"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni görüşmeler kayan simgeler veya baloncuk olarak görünür. Açmak için baloncuğa dokunun. Baloncuğu taşımak için sürükleyin."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Baloncukları istediğiniz zaman kontrol edin"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bu uygulamanın baloncuklarını kapatmak için Yönet\'e dokunun"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Son kapatılan baloncuk yok"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son baloncuklar ve kapattığınız baloncuklar burada görünür"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
new file mode 100644
index 0000000..8feb319
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıksız program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP\'yi kapat"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
new file mode 100644
index 0000000..6d012af
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Закрити"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Розгорнути"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Налаштування"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"У додатку <xliff:g id="NAME">%s</xliff:g> є функція \"Картинка в картинці\""</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Щоб додаток <xliff:g id="NAME">%s</xliff:g> не використовував цю функцію, вимкніть її в налаштуваннях."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Відтворити"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Призупинити"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти далі"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти назад"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змінити розмір"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Додаток не підтримує розділення екрана."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Додаток не підтримує запуск на додаткових екранах."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Розділювач екрана"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ліве вікно на весь екран"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ліве вікно на 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ліве вікно на 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ліве вікно на 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Праве вікно на весь екран"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Верхнє вікно на весь екран"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Верхнє вікно на 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Верхнє вікно на 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Верхнє вікно на 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Нижнє вікно на весь екран"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Налаштування спливаючих чатів від додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Додаткове меню"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Додати в список"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"Cповіщення \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" від додатка <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"Сповіщення \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" від додатка <xliff:g id="APP_NAME">%2$s</xliff:g> (і ще <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>)"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перемістити ліворуч угору"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перемістити праворуч угору"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перемістити ліворуч униз"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перемістити праворуч униз"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Закрити підказку"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показувати спливаючі чати для розмов"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Спливаючий чат"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нові повідомлення чату з\'являються у вигляді спливаючих значків. Щоб відкрити чат, натисніть його, а щоб перемістити – перетягніть."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контроль спливаючих чатів"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Натисніть \"Налаштувати\", щоб вимкнути спливаючі чати від цього додатка"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Немає нещодавніх спливаючих чатів"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Тут з\'являтимуться нещодавні й закриті спливаючі чати"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
new file mode 100644
index 0000000..0cc15452
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без назви)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
new file mode 100644
index 0000000..cd6529f0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"بند کریں"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"پھیلائیں"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ترتیبات"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"مینو"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> تصویر میں تصویر میں ہے"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"اگر آپ نہیں چاہتے ہیں کہ <xliff:g id="NAME">%s</xliff:g> اس خصوصیت کا استعمال کرے تو ترتیبات کھولنے کے لیے تھپتھپا کر اسے آف کرے۔"</string>
+ <string name="pip_play" msgid="3496151081459417097">"چلائیں"</string>
+ <string name="pip_pause" msgid="690688849510295232">"موقوف کریں"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ایپ ثانوی ڈسپلیز پر شروعات کا تعاون نہیں کرتی۔"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"سپلٹ اسکرین تقسیم کار"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"بائیں فل اسکرین"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"بائیں %70"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"بائیں %50"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"بائیں %30"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"دائیں فل اسکرین"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"بالائی فل اسکرین"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"اوپر %70"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"اوپر %50"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"اوپر %30"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"نچلی فل اسکرین"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> بلبلوں کے لیے ترتیبات"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"اوورفلو"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"انبار میں واپس شامل کریں"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> کی جانب سے <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> اور <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> مزید سے <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"اوپر بائیں جانب لے جائیں"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"اوپر دائیں جانب لے جائيں"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نیچے بائیں جانب لے جائیں"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نیچے دائیں جانب لے جائیں"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ترتیبات"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"بلبلہ برخاست کریں"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"گفتگو بلبلہ نہ کریں"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"بلبلے کے ذریعے چیٹ کریں"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کسی بھی وقت بلبلے کو کنٹرول کریں"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"اس ایپ سے بلبلوں کو آف کرنے کے لیے نظم کریں پر تھپتھپائیں"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
new file mode 100644
index 0000000..64e5db5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP بند کریں"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
new file mode 100644
index 0000000..b714f59
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Yopish"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Yoyish"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Sozlamalar"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> tasvir ustida tasvir rejimida"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ilovasi uchun bu funksiyani sozlamalar orqali faolsizlantirish mumkin."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Ijro"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauza"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Keyingisiga o‘tish"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Avvalgisiga qaytish"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Oʻlchamini oʻzgartirish"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Bu ilova qo‘shimcha ekranlarda ishga tushmaydi."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ekranni ikkiga bo‘lish chizig‘i"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Chapda to‘liq ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Chapda 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Chapda 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Chapda 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"O‘ngda to‘liq ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Tepada to‘liq ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Tepada 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Tepada 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Tepada 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pastda to‘liq ekran"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> bulutchalari uchun sozlamalar"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kengaytirilgan"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yana toʻplamga kiritish"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ilovasidan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> va yana <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ta bildirishnoma"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Yuqori chapga surish"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuqori oʻngga surish"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Quyi chapga surish"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oʻngga surish"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bulutchalar yordamida subhatlashish"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yangi xabarlar qalqib chiquvchi belgilar yoki bulutchalar kabi chiqadi. Xabarni ochish uchun bildirishnoma ustiga bosing. Xabarni qayta joylash uchun bildirishnomani suring."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bulutchalardagi bildirishnomalar"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bu ilova bulutchalarini faolsizlantirish uchun Boshqarish tugmasini bosing"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Avvalgi bulutchalar topilmadi"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bu yerda oxirgi va yopilgan bulutcha shaklidagi bildirishnomalar chiqadi"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
new file mode 100644
index 0000000..a0abb3d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
new file mode 100644
index 0000000..be6f844
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Đóng"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Mở rộng"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Cài đặt"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> đang ở chế độ ảnh trong ảnh"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Nếu bạn không muốn <xliff:g id="NAME">%s</xliff:g> sử dụng tính năng này, hãy nhấn để mở cài đặt và tắt tính năng này."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Phát"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Tạm dừng"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Chuyển tới mục tiếp theo"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Chuyển về mục trước"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Đổi kích thước"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Ứng dụng không hỗ trợ khởi chạy trên màn hình phụ."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bộ chia chia đôi màn hình"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Toàn màn hình bên trái"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Trái 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Trái 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Trái 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Toàn màn hình bên phải"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Toàn màn hình phía trên"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Trên 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Trên 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Trên 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Toàn màn hình phía dưới"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Tùy chọn cài đặt cho bong bóng trò chuyện <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Trình đơn mục bổ sung"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Thêm lại vào ngăn xếp"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> của <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> từ <xliff:g id="APP_NAME">%2$s</xliff:g> và <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> bong bóng khác"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Chuyển lên trên cùng bên trái"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyển lên trên cùng bên phải"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyển tới dưới cùng bên trái"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyển tới dưới cùng bên phải"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Đóng bong bóng"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Trò chuyện bằng bong bóng trò chuyện"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kiểm soát bong bóng bất cứ lúc nào"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Nhấn vào nút Quản lý để tắt bong bóng trò chuyện từ ứng dụng này"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Không có bong bóng trò chuyện nào gần đây"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bong bóng trò chuyện đã đóng và bong bóng trò chuyện gần đây sẽ xuất hiện ở đây"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
new file mode 100644
index 0000000..093ef13
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Không có chương trình tiêu đề)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Đóng PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..b17e35e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"关闭"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展开"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"设置"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"菜单"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>目前位于“画中画”中"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"如果您不想让“<xliff:g id="NAME">%s</xliff:g>”使用此功能,请点按以打开设置,然后关闭此功能。"</string>
+ <string name="pip_play" msgid="3496151081459417097">"播放"</string>
+ <string name="pip_pause" msgid="690688849510295232">"暂停"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一个"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一个"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"调整大小"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"应用不支持在辅显示屏上启动。"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"分屏分隔线"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"左侧全屏"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"左侧 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"左侧 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"左侧 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"右侧全屏"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"顶部全屏"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"顶部 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"顶部 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"顶部 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"底部全屏"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>对话泡的设置"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"菜单"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"重新加入叠放"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g>和另外 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 个应用:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"移至左上角"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上角"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下角"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下角"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"关闭对话泡"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不以对话泡形式显示对话"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"使用对话泡聊天"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新对话会以浮动图标或对话泡形式显示。点按即可打开对话泡。拖动即可移动对话泡。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"随时控制对话泡"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"点按“管理”按钮,可关闭来自此应用的对话泡"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有对话泡"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的对话泡和已关闭的对话泡"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
new file mode 100644
index 0000000..0b65fd3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..88d28df
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"關閉"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"選單"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"「<xliff:g id="NAME">%s</xliff:g>」目前在畫中畫模式"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"如果您不想「<xliff:g id="NAME">%s</xliff:g>」使用此功能,請輕按以開啟設定,然後停用此功能。"</string>
+ <string name="pip_play" msgid="3496151081459417097">"播放"</string>
+ <string name="pip_pause" msgid="690688849510295232">"暫停"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"應用程式無法在次要顯示屏上啟動。"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"分割畫面分隔線"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"左邊全螢幕"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"左邊 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"左邊 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"左邊 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"右邊全螢幕"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"頂部全螢幕"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"頂部 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"頂部 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"頂部 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"底部全螢幕"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」小視窗設定"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"顯示更多"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"加回堆疊"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"來自「<xliff:g id="APP_NAME">%2$s</xliff:g>」的 <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"來自「<xliff:g id="APP_NAME">%2$s</xliff:g>」及另外 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 個應用程式的<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"移去左上角"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移去右上角"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移去左下角"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移去右下角"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉小視窗氣泡"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要透過小視窗顯示對話"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"使用小視窗進行即時通訊"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新對話會以浮動圖示 (小視窗) 顯示。輕按即可開啟小視窗。拖曳即可移動小視窗。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"隨時控制小視窗設定"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"輕按「管理」即可關閉此應用程式的小視窗"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"沒有最近曾使用的小視窗"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近使用和關閉的小視窗會在這裡顯示"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
new file mode 100644
index 0000000..74fc374
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(沒有標題的節目)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..2530db7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"關閉"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"選單"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"「<xliff:g id="NAME">%s</xliff:g>」目前在子母畫面中"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"如果你不想讓「<xliff:g id="NAME">%s</xliff:g>」使用這項功能,請輕觸開啟設定頁面,然後停用此功能。"</string>
+ <string name="pip_play" msgid="3496151081459417097">"播放"</string>
+ <string name="pip_pause" msgid="690688849510295232">"暫停"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"應用程式無法在次要顯示器上啟動。"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"分割畫面分隔線"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"以全螢幕顯示左側畫面"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"以 70% 的螢幕空間顯示左側畫面"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"以 50% 的螢幕空間顯示左側畫面"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"以 30% 的螢幕空間顯示左側畫面"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"以全螢幕顯示右側畫面"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"以全螢幕顯示頂端畫面"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"以 70% 的螢幕空間顯示頂端畫面"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"以 50% 的螢幕空間顯示頂端畫面"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"以 30% 的螢幕空間顯示頂端畫面"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"以全螢幕顯示底部畫面"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」對話框的設定"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"溢位"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"重新加入堆疊"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"「<xliff:g id="APP_NAME">%2$s</xliff:g>」和其他 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 個應用程式:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"移至左上方"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上方"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下方"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下方"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉對話框"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要以對話框形式顯示對話"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"透過對話框來聊天"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新的對話會以浮動圖示或對話框形式顯示。輕觸即可開啟對話框,拖曳則可移動對話框。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"你隨時可以控管對話框的各項設定"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"輕觸 [管理] 即可關閉來自這個應用程式的對話框"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近沒有任何對話框"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近的對話框和已關閉的對話框會顯示在這裡"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
new file mode 100644
index 0000000..e7d1ca98
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無標題的節目)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
new file mode 100644
index 0000000..72fdc9c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Vala"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Nweba"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Izilungiselelo"</string>
+ <string name="pip_menu_title" msgid="5393619322111827096">"Imenyu"</string>
+ <string name="pip_notification_title" msgid="1347104727641353453">"U-<xliff:g id="NAME">%s</xliff:g> ungaphakathi kwesithombe esiphakathi kwesithombe"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Uma ungafuni i-<xliff:g id="NAME">%s</xliff:g> ukuthi isebenzise lesi sici, thepha ukuze uvule izilungiselelo uphinde uyivale."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Dlala"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Misa isikhashana"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Yeqela kokulandelayo"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Yeqela kokwangaphambilini"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Shintsha usayizi"</string>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uhlelo lokusebenza alusekeli ukuqalisa kuzibonisi zesibili."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Isihlukanisi sokuhlukanisa isikrini"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Isikrini esigcwele esingakwesokunxele"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kwesokunxele ngo-70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kwesokunxele ngo-50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kwesokunxele ngo-30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Isikrini esigcwele esingakwesokudla"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Isikrini esigcwele esiphezulu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Okuphezulu okungu-70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Okuphezulu okungu-50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Okuphezulu okungu-30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ngaphansi kwesikrini esigcwele"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Izilungiselelo zamabhamuza e-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ukuphuphuma"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Engeza emuva kusitaki"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"I-<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"I-<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kusukela ku-<xliff:g id="APP_NAME">%2$s</xliff:g> nokungu-<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ngaphezulu"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Hambisa phezulu kwesokunxele"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Xoxa usebenzisa amabhamuza"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Izingxoxo ezintsha zivela njengezithonjana ezintantayo, noma amabhamuza. Thepha ukuze uvule ibhamuza. Hudula ukuze ulihambise."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Lawula amabhamuza noma nini"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Thepha okuthi Phatha ukuvala amabhamuza kusuka kulolu hlelo lokusebenza"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Awekho amabhamuza akamuva"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Amabhamuza akamuva namabhamuza asusiwe azobonakala lapha."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
new file mode 100644
index 0000000..62fc4c4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Alukho uhlelo lwesihloko)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Vala i-PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
index bb9accd..13089af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
@@ -16,6 +16,7 @@
package com.android.wm.shell;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
@@ -34,17 +35,20 @@
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutout;
private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<AppPairs> mAppPairsOptional;
public ShellDump(ShellTaskOrganizer shellTaskOrganizer,
Optional<SplitScreen> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout) {
+ Optional<HideDisplayCutout> hideDisplayCutout,
+ Optional<AppPairs> appPairsOptional) {
mShellTaskOrganizer = shellTaskOrganizer;
mSplitScreenOptional = splitScreenOptional;
mPipOptional = pipOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutout = hideDisplayCutout;
+ mAppPairsOptional = appPairsOptional;
}
public void dump(PrintWriter pw) {
@@ -55,5 +59,8 @@
mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
+ pw.println();
+ pw.println();
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
index 4269a90..d654f8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
@@ -16,6 +16,7 @@
package com.android.wm.shell;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -31,15 +32,18 @@
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<AppPairs> mAppPairsOptional;
public ShellInit(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<SplitScreen> splitScreenOptional) {
+ Optional<SplitScreen> splitScreenOptional,
+ Optional<AppPairs> appPairsOptional) {
mDisplayImeController = displayImeController;
mDragAndDropController = dragAndDropController;
mShellTaskOrganizer = shellTaskOrganizer;
mSplitScreenOptional = splitScreenOptional;
+ mAppPairsOptional = appPairsOptional;
}
public void init() {
@@ -47,6 +51,7 @@
mDisplayImeController.startMonitorDisplays();
// Register the shell organizer
mShellTaskOrganizer.registerOrganizer();
+ mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
// Bind the splitscreen impl to the drag drop controller
mDragAndDropController.setSplitScreenController(mSplitScreenOptional);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index ece063c..006fd9d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -26,7 +26,6 @@
import android.annotation.IntDef;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -39,6 +38,7 @@
import android.window.TaskOrganizer;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -309,6 +309,15 @@
}
}
+ /** Gets running task by taskId. Returns {@code null} if no such task observed. */
+ @Nullable
+ public RunningTaskInfo getRunningTaskInfo(int taskId) {
+ synchronized (mLock) {
+ final TaskAppearedInfo info = mTasks.get(taskId);
+ return info != null ? info.getTaskInfo() : null;
+ }
+ }
+
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
new file mode 100644
index 0000000..d30acee
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import java.io.PrintWriter;
+
+/**
+ * An app-pairs consisting of {@link #mRootTaskInfo} that acts as the hierarchy parent of
+ * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
+ * Also includes all UI for managing the pair like the divider.
+ */
+// TODO: Add divider
+// TODO: Handle display rotation
+class AppPair implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = AppPair.class.getSimpleName();
+
+ private ActivityManager.RunningTaskInfo mRootTaskInfo;
+ private SurfaceControl mRootTaskLeash;
+ private ActivityManager.RunningTaskInfo mTaskInfo1;
+ private SurfaceControl mTaskLeash1;
+ private ActivityManager.RunningTaskInfo mTaskInfo2;
+ private SurfaceControl mTaskLeash2;
+
+ private final AppPairsController mController;
+ private final SyncTransactionQueue mSyncQueue;
+
+ AppPair(AppPairsController controller) {
+ mController = controller;
+ mSyncQueue = controller.getSyncTransactionQueue();
+ }
+
+ int getRootTaskId() {
+ return mRootTaskInfo != null ? mRootTaskInfo.taskId : INVALID_TASK_ID;
+ }
+
+ private int getTaskId1() {
+ return mTaskInfo1 != null ? mTaskInfo1.taskId : INVALID_TASK_ID;
+ }
+
+ private int getTaskId2() {
+ return mTaskInfo2 != null ? mTaskInfo2.taskId : INVALID_TASK_ID;
+ }
+
+ boolean contains(int taskId) {
+ return taskId == getRootTaskId() || taskId == getTaskId1() || taskId == getTaskId2();
+ }
+
+ boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
+ task1.taskId, task2.taskId, this);
+
+ if (!task1.isResizeable || !task2.isResizeable) {
+ ProtoLog.e(WM_SHELL_TASK_ORG,
+ "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
+ task1.isResizeable, task2.isResizeable);
+ return false;
+ }
+
+ mTaskInfo1 = task1;
+ mTaskInfo2 = task2;
+
+ // TODO: properly calculate bounds for pairs.
+ final Rect rootBounds = mRootTaskInfo.configuration.windowConfiguration.getBounds();
+ final Rect bounds1 = new Rect(
+ rootBounds.left, rootBounds.top, rootBounds.right / 2, rootBounds.bottom / 2);
+ final Rect bounds2 = new Rect(
+ bounds1.right, bounds1.bottom, rootBounds.right, rootBounds.bottom);
+ final WindowContainerToken token1 = task1.token;
+ final WindowContainerToken token2 = task2.token;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ wct.setHidden(mRootTaskInfo.token, false)
+ .reparent(token1, mRootTaskInfo.token, true /* onTop */)
+ .reparent(token2, mRootTaskInfo.token, true /* onTop */)
+ .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW)
+ .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW)
+ .setBounds(token1, bounds1)
+ .setBounds(token2, bounds2)
+ // Moving the root task to top after the child tasks were repareted , or the root
+ // task cannot be visible and focused.
+ .reorder(mRootTaskInfo.token, true);
+ mController.getTaskOrganizer().applyTransaction(wct);
+ return true;
+ }
+
+ void unpair() {
+ final WindowContainerToken token1 = mTaskInfo1.token;
+ final WindowContainerToken token2 = mTaskInfo2.token;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ // Reparent out of this container and reset windowing mode.
+ wct.setHidden(mRootTaskInfo.token, true)
+ .reorder(mRootTaskInfo.token, false)
+ .reparent(token1, null, false /* onTop */)
+ .reparent(token2, null, false /* onTop */)
+ .setWindowingMode(token1, WINDOWING_MODE_UNDEFINED)
+ .setWindowingMode(token2, WINDOWING_MODE_UNDEFINED);
+ mController.getTaskOrganizer().applyTransaction(wct);
+
+ mTaskInfo1 = null;
+ mTaskInfo2 = null;
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) {
+ mRootTaskInfo = taskInfo;
+ mRootTaskLeash = leash;
+ } else if (taskInfo.taskId == getTaskId1()) {
+ mTaskInfo1 = taskInfo;
+ mTaskLeash1 = leash;
+ } else if (taskInfo.taskId == getTaskId2()) {
+ mTaskInfo2 = taskInfo;
+ mTaskLeash2 = leash;
+ } else {
+ throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
+ }
+
+ if (mTaskLeash1 == null || mTaskLeash2 == null) return;
+
+ // TODO: Is there more we need to do here?
+ mSyncQueue.runInSync(t -> t
+ .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
+ mTaskInfo1.positionInParent.y)
+ .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
+ mTaskInfo2.positionInParent.y)
+ .show(mRootTaskLeash)
+ .show(mTaskLeash1)
+ .show(mTaskLeash2));
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskId == getRootTaskId()) {
+ mRootTaskInfo = taskInfo;
+ } else if (taskInfo.taskId == getTaskId1()) {
+ mTaskInfo1 = taskInfo;
+ } else if (taskInfo.taskId == getTaskId2()) {
+ mTaskInfo2 = taskInfo;
+ } else {
+ throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskId == getRootTaskId()) {
+ // We don't want to release this object back to the pool since the root task went away.
+ mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
+ } else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) {
+ mController.unpair(mRootTaskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+ pw.println(innerPrefix + "Root taskId=" + getRootTaskId()
+ + " winMode=" + mRootTaskInfo.getWindowingMode());
+ if (mTaskInfo1 != null) {
+ pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId
+ + " winMode=" + mTaskInfo1.getWindowingMode());
+ }
+ if (mTaskInfo2 != null) {
+ pw.println(innerPrefix + "2 taskId=" + mTaskInfo2.taskId
+ + " winMode=" + mTaskInfo2.getWindowingMode());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return TAG + "#" + getRootTaskId();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
new file mode 100644
index 0000000..ef3e3e0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import android.app.ActivityManager;
+
+import androidx.annotation.NonNull;
+
+import java.io.PrintWriter;
+
+/**
+ * Interface to engage app pairs feature.
+ */
+public interface AppPairs {
+ /** Pairs indicated tasks. */
+ boolean pair(int task1, int task2);
+ /** Pairs indicated tasks. */
+ boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2);
+ /** Unpairs any app-pair containing this task id. */
+ void unpair(int taskId);
+ /** Dumps current status of app pairs. */
+ void dump(@NonNull PrintWriter pw, String prefix);
+ /** Called when the shell organizer has been registered. */
+ void onOrganizerRegistered();
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
new file mode 100644
index 0000000..e0c7ba9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import android.app.ActivityManager;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import java.io.PrintWriter;
+
+/**
+ * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
+ */
+public class AppPairsController implements AppPairs {
+ private static final String TAG = AppPairsController.class.getSimpleName();
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final SyncTransactionQueue mSyncQueue;
+
+ private AppPairsPool mPairsPool;
+ // Active app-pairs mapped by root task id key.
+ private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
+
+ public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
+ mTaskOrganizer = organizer;
+ mSyncQueue = syncQueue;
+ }
+
+ @Override
+ public void onOrganizerRegistered() {
+ if (mPairsPool == null) {
+ setPairsPool(new AppPairsPool(this));
+ }
+ }
+
+ @VisibleForTesting
+ void setPairsPool(AppPairsPool pool) {
+ mPairsPool = pool;
+ }
+
+ @Override
+ public boolean pair(int taskId1, int taskId2) {
+ final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
+ final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
+ if (task1 == null || task2 == null) {
+ return false;
+ }
+ return pair(task1, task2);
+ }
+
+ @Override
+ public boolean pair(ActivityManager.RunningTaskInfo task1,
+ ActivityManager.RunningTaskInfo task2) {
+ return pairInner(task1, task2) != null;
+ }
+
+ @VisibleForTesting
+ AppPair pairInner(
+ @NonNull ActivityManager.RunningTaskInfo task1,
+ @NonNull ActivityManager.RunningTaskInfo task2) {
+ final AppPair pair = mPairsPool.acquire();
+ if (!pair.pair(task1, task2)) {
+ mPairsPool.release(pair);
+ return null;
+ }
+
+ mActiveAppPairs.put(pair.getRootTaskId(), pair);
+ return pair;
+ }
+
+ @Override
+ public void unpair(int taskId) {
+ unpair(taskId, true /* releaseToPool */);
+ }
+
+ void unpair(int taskId, boolean releaseToPool) {
+ AppPair pair = mActiveAppPairs.get(taskId);
+ if (pair == null) {
+ for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
+ final AppPair candidate = mActiveAppPairs.valueAt(i);
+ if (candidate.contains(taskId)) {
+ pair = candidate;
+ break;
+ }
+ }
+ }
+ if (pair == null) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "taskId %d isn't isn't in an app-pair.", taskId);
+ return;
+ }
+
+ ProtoLog.v(WM_SHELL_TASK_ORG, "unpair taskId=%d pair=%s", taskId, pair);
+ mActiveAppPairs.remove(pair.getRootTaskId());
+ pair.unpair();
+ if (releaseToPool) {
+ mPairsPool.release(pair);
+ }
+ }
+
+ ShellTaskOrganizer getTaskOrganizer() {
+ return mTaskOrganizer;
+ }
+
+ SyncTransactionQueue getSyncTransactionQueue() {
+ return mSyncQueue;
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+
+ for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
+ mActiveAppPairs.valueAt(i).dump(pw, childPrefix);
+ }
+
+ if (mPairsPool != null) {
+ mPairsPool.dump(pw, prefix);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return TAG + "#" + mActiveAppPairs.size();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java
new file mode 100644
index 0000000..5c6037e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Class that manager pool of {@link AppPair} objects. Helps reduce the need to call system_server
+ * to create a root task for the app-pair when needed since we always have one ready to go.
+ */
+class AppPairsPool {
+ private static final String TAG = AppPairsPool.class.getSimpleName();
+
+ @VisibleForTesting
+ final AppPairsController mController;
+ // The pool
+ private final ArrayList<AppPair> mPool = new ArrayList();
+
+ AppPairsPool(AppPairsController controller) {
+ mController = controller;
+ incrementPool();
+ }
+
+ AppPair acquire() {
+ final AppPair entry = mPool.remove(mPool.size() - 1);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "acquire entry.taskId=%s listener=%s size=%d",
+ entry.getRootTaskId(), entry, mPool.size());
+ if (mPool.size() == 0) {
+ incrementPool();
+ }
+ return entry;
+ }
+
+ void release(AppPair entry) {
+ mPool.add(entry);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "release entry.taskId=%s listener=%s size=%d",
+ entry.getRootTaskId(), entry, mPool.size());
+ }
+
+ @VisibleForTesting
+ void incrementPool() {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "incrementPool size=%d", mPool.size());
+ final AppPair entry = new AppPair(mController);
+ // TODO: multi-display...
+ mController.getTaskOrganizer().createRootTask(
+ DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN, entry);
+ mPool.add(entry);
+ }
+
+ @VisibleForTesting
+ int poolSize() {
+ return mPool.size();
+ }
+
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+ for (int i = mPool.size() - 1; i >= 0; --i) {
+ mPool.get(i).dump(pw, childPrefix);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return TAG + "#" + mPool.size();
+ }
+}
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 78f7e25..ce1139b 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
@@ -54,6 +54,11 @@
private static final String TAG = PipBoundsState.class.getSimpleName();
private final @NonNull Rect mBounds = new Rect();
+ private final @NonNull Rect mMovementBounds = new Rect();
+ private final @NonNull Rect mNormalBounds = new Rect();
+ private final @NonNull Rect mExpandedBounds = new Rect();
+ private final @NonNull Rect mNormalMovementBounds = new Rect();
+ private final @NonNull Rect mExpandedMovementBounds = new Rect();
private final Context mContext;
private float mAspectRatio;
private int mStashedState = STASH_TYPE_NONE;
@@ -92,9 +97,7 @@
.getDimensionPixelSize(R.dimen.pip_stash_offset);
}
- /**
- * Set the current PIP bounds.
- */
+ /** Set the current PIP bounds. */
public void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
}
@@ -104,6 +107,53 @@
return new Rect(mBounds);
}
+ /** Returns the current movement bounds. */
+ public Rect getMovementBounds() {
+ return mMovementBounds;
+ }
+
+ /** Set the current normal PIP bounds. */
+ public void setNormalBounds(@NonNull Rect bounds) {
+ mNormalBounds.set(bounds);
+ }
+
+ /** Get the current normal PIP bounds. */
+ @NonNull
+ public Rect getNormalBounds() {
+ return mNormalBounds;
+ }
+
+ /** Set the expanded bounds of PIP. */
+ public void setExpandedBounds(@NonNull Rect bounds) {
+ mExpandedBounds.set(bounds);
+ }
+
+ /** Get the PIP expanded bounds. */
+ @NonNull
+ public Rect getExpandedBounds() {
+ return mExpandedBounds;
+ }
+
+ /** Set the normal movement bounds. */
+ public void setNormalMovementBounds(Rect bounds) {
+ mNormalMovementBounds.set(bounds);
+ }
+
+ /** Returns the normal movement bounds. */
+ public Rect getNormalMovementBounds() {
+ return mNormalMovementBounds;
+ }
+
+ /** Set the expanded movement bounds. */
+ public void setExpandedMovementBounds(Rect bounds) {
+ mExpandedMovementBounds.set(bounds);
+ }
+
+ /** Returns the expanded movement bounds. */
+ public Rect getExpandedMovementBounds() {
+ return mExpandedMovementBounds;
+ }
+
/**
* Dictate where PiP currently should be stashed, if at all.
*/
@@ -389,6 +439,11 @@
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + "mBounds=" + mBounds);
+ pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
+ pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
+ pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
+ pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
+ pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
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 1f67d2c..598b5d9 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
@@ -87,7 +87,6 @@
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
- private final Rect mTmpNormalBounds = new Rect();
protected final Rect mReentryBounds = new Rect();
private boolean mIsInFixedRotation;
@@ -114,12 +113,12 @@
// TODO: Technically this should account for movement animation bounds as well
Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
final boolean changed = onDisplayRotationChanged(mContext,
- mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation,
- toRotation, t);
+ mPipBoundsState.getNormalBounds(), currentBounds, mTmpInsetBounds, displayId,
+ fromRotation, toRotation, t);
if (changed) {
// If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
// movement bounds
- mTouchHandler.adjustBoundsForRotation(mTmpNormalBounds,
+ mTouchHandler.adjustBoundsForRotation(mPipBoundsState.getNormalBounds(),
mPipBoundsState.getBounds(), mTmpInsetBounds);
// The bounds are being applied to a specific snap fraction, so reset any known offsets
@@ -134,7 +133,7 @@
mTouchHandler.onImeVisibilityChanged(false, 0);
}
- updateMovementBounds(mTmpNormalBounds, true /* fromRotation */,
+ updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */,
false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t);
}
};
@@ -511,7 +510,7 @@
mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
- mTmpNormalBounds.set(mPipBoundsAlgorithm.getNormalBounds());
+ mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
if (outBounds.isEmpty()) {
outBounds.set(mPipBoundsAlgorithm.getDefaultBounds());
}
@@ -519,7 +518,7 @@
// mTouchHandler would rely on the bounds populated from mPipTaskOrganizer
mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
fromShelfAdjustment, wct);
- mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBoundsState.getNormalBounds(),
outBounds, fromImeAdjustment, fromShelfAdjustment,
mTmpDisplayInfo.rotation);
}
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 d7b56ae..e4e1211 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
@@ -75,9 +75,6 @@
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- /** The bounds within which PIP's top-left coordinate is allowed to move. */
- private final Rect mMovementBounds = new Rect();
-
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
@@ -339,13 +336,12 @@
}
/** Sets the movement bounds to use to constrain PIP position animations. */
- void setCurrentMovementBounds(Rect movementBounds) {
- mMovementBounds.set(movementBounds);
+ void onMovementBoundsChanged() {
rebuildFlingConfigs();
// 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.set(mPipBoundsState.getMovementBounds());
mFloatingAllowedArea.right += getBounds().width();
mFloatingAllowedArea.bottom += getBounds().height();
}
@@ -395,10 +391,10 @@
final float leftEdge = isStash
? mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width()
- : mMovementBounds.left;
+ : mPipBoundsState.getMovementBounds().left;
final float rightEdge = isStash
? mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset()
- : mMovementBounds.right;
+ : mPipBoundsState.getMovementBounds().right;
final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
@@ -433,7 +429,7 @@
// Animate off the bottom of the screen, then dismiss PIP.
mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_Y,
- mMovementBounds.bottom + getBounds().height() * 2,
+ mPipBoundsState.getMovementBounds().bottom + getBounds().height() * 2,
0,
mSpringConfig)
.withEndActions(this::dismissPip);
@@ -504,10 +500,12 @@
/** Set new fling configs whose min/max values respect the given movement bounds. */
private void rebuildFlingConfigs() {
- mFlingConfigX = new PhysicsAnimator.FlingConfig(
- DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
- mFlingConfigY = new PhysicsAnimator.FlingConfig(
- DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
+ mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
+ mPipBoundsState.getMovementBounds().left,
+ mPipBoundsState.getMovementBounds().right);
+ mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
+ mPipBoundsState.getMovementBounds().top,
+ mPipBoundsState.getMovementBounds().bottom);
mStashConfigX = new PhysicsAnimator.FlingConfig(
DEFAULT_FRICTION,
mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 4cc41d9..609fa35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -88,16 +88,8 @@
*/
private boolean mEnableStash = true;
- // The current movement bounds
- private Rect mMovementBounds = new Rect();
-
// The reference inset bounds, used to determine the dismiss fraction
private Rect mInsetBounds = new Rect();
- // The reference bounds used to calculate the normal/expanded target bounds
- private Rect mNormalBounds = new Rect();
- @VisibleForTesting public Rect mNormalMovementBounds = new Rect();
- private Rect mExpandedBounds = new Rect();
- @VisibleForTesting public Rect mExpandedMovementBounds = new Rect();
private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
@@ -316,7 +308,8 @@
final Rect toMovementBounds = new Rect();
mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
toMovementBounds, 0);
- final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+ final int prevBottom = mPipBoundsState.getMovementBounds().bottom
+ - mMovementBoundsExtraOffsets;
if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) {
outBounds.offsetTo(outBounds.left, toMovementBounds.bottom);
}
@@ -344,16 +337,15 @@
}
// Re-calculate the expanded bounds
- mNormalBounds.set(normalBounds);
Rect normalMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mNormalBounds, insetBounds,
+ mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(normalBounds, insetBounds,
normalMovementBounds, bottomOffset);
- if (mMovementBounds.isEmpty()) {
+ if (mPipBoundsState.getMovementBounds().isEmpty()) {
// mMovementBounds is not initialized yet and a clean movement bounds without
// bottom offset shall be used later in this function.
mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
- mMovementBounds, 0 /* bottomOffset */);
+ mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */);
}
// Calculate the expanded size
@@ -362,13 +354,16 @@
mContext.getDisplay().getRealSize(displaySize);
Size expandedSize = mPipBoundsAlgorithm.getSnapAlgorithm().getSizeForAspectRatio(
aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
- mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
+ mPipBoundsState.setExpandedBounds(
+ new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
Rect expandedMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mExpandedBounds, insetBounds,
- expandedMovementBounds, bottomOffset);
+ mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(
+ mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
+ bottomOffset);
- mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height());
- mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height());
+ mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
+ mPipBoundsState.getExpandedBounds().height());
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -387,7 +382,8 @@
final Rect toMovementBounds = new Rect();
mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
toMovementBounds, mIsImeShowing ? mImeHeight : 0);
- final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+ final int prevBottom = mPipBoundsState.getMovementBounds().bottom
+ - mMovementBoundsExtraOffsets;
// This is to handle landscape fullscreen IMEs, don't apply the extra offset in this
// case
final int toBottom = toMovementBounds.bottom < toMovementBounds.top
@@ -395,7 +391,7 @@
: toMovementBounds.bottom - extraOffset;
if (isExpanded) {
- curBounds.set(mExpandedBounds);
+ curBounds.set(mPipBoundsState.getExpandedBounds());
mPipBoundsAlgorithm.getSnapAlgorithm().applySnapFraction(curBounds,
toMovementBounds, mSavedSnapFraction);
}
@@ -416,19 +412,21 @@
// Update the movement bounds after doing the calculations based on the old movement bounds
// above
- mNormalMovementBounds.set(normalMovementBounds);
- mExpandedMovementBounds.set(expandedMovementBounds);
+ mPipBoundsState.setNormalMovementBounds(normalMovementBounds);
+ mPipBoundsState.setExpandedMovementBounds(expandedMovementBounds);
mDisplayRotation = displayRotation;
mInsetBounds.set(insetBounds);
updateMovementBounds();
mMovementBoundsExtraOffsets = extraOffset;
- mConnection.onMovementBoundsChanged(mNormalBounds, mExpandedBounds, mNormalMovementBounds,
- mExpandedMovementBounds);
+ mConnection.onMovementBoundsChanged(normalBounds, mPipBoundsState.getExpandedBounds(),
+ mPipBoundsState.getNormalMovementBounds(),
+ mPipBoundsState.getExpandedMovementBounds());
// If we have a deferred resize, apply it now
if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, true /* immediate */);
+ mPipBoundsState.getNormalMovementBounds(), mPipBoundsState.getMovementBounds(),
+ true /* immediate */);
mSavedSnapFraction = -1f;
mDeferResizeToNormalBoundsUntilRotation = -1;
}
@@ -652,9 +650,10 @@
}
private void animateToExpandedState(Runnable callback) {
- Rect expandedBounds = new Rect(mExpandedBounds);
+ Rect expandedBounds = new Rect(mPipBoundsState.getExpandedBounds());
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
- mMovementBounds, mExpandedMovementBounds, callback);
+ mPipBoundsState.getMovementBounds(), mPipBoundsState.getExpandedMovementBounds(),
+ callback);
}
private void animateToUnexpandedState(Rect restoreBounds) {
@@ -662,7 +661,7 @@
mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(restoreBounds,
mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
- restoredMovementBounds, mMovementBounds, false /* immediate */);
+ restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */);
mSavedSnapFraction = -1f;
}
@@ -688,13 +687,6 @@
mMotionHelper = pipMotionHelper;
}
- /**
- * @return the unexpanded bounds.
- */
- public Rect getNormalBounds() {
- return mNormalBounds;
- }
-
Rect getUserResizeBounds() {
return mPipResizeGestureHandler.getUserResizeBounds();
}
@@ -717,7 +709,8 @@
Rect bounds = getPossiblyAnimatingBounds();
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
- mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
+ mMovementWithinDismiss = touchState.getDownTouchPosition().y
+ >= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
mDownSavedFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
@@ -759,7 +752,7 @@
final PointF curPos = touchState.getLastTouchPosition();
if (mMovementWithinDismiss) {
// Track if movement remains near the bottom edge to identify swipe to dismiss
- mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
+ mMovementWithinDismiss = curPos.y >= mPipBoundsState.getMovementBounds().bottom;
}
return true;
}
@@ -804,15 +797,17 @@
} else if (mTouchState.isDoubleTap() && !mPipBoundsState.isStashed()) {
// If using pinch to zoom, double-tap functions as resizing between max/min size
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- final boolean toExpand =
- mMotionHelper.getBounds().width() < mExpandedBounds.width()
- && mMotionHelper.getBounds().height() < mExpandedBounds.height();
- mPipResizeGestureHandler.setUserResizeBounds(toExpand ? mExpandedBounds
- : mNormalBounds);
+ final boolean toExpand = mMotionHelper.getBounds().width()
+ < mPipBoundsState.getExpandedBounds().width()
+ && mMotionHelper.getBounds().height()
+ < mPipBoundsState.getExpandedBounds().height();
+ mPipResizeGestureHandler.setUserResizeBounds(toExpand
+ ? mPipBoundsState.getExpandedBounds()
+ : mPipBoundsState.getNormalBounds());
if (toExpand) {
animateToExpandedState(null);
} else {
- animateToUnexpandedState(mNormalBounds);
+ animateToUnexpandedState(mPipBoundsState.getNormalBounds());
}
} else {
// Expand to fullscreen if this is a double tap
@@ -870,8 +865,8 @@
*/
private void updateMovementBounds() {
mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mMotionHelper.getBounds(),
- mInsetBounds, mMovementBounds, mIsImeShowing ? mImeHeight : 0);
- mMotionHelper.setCurrentMovementBounds(mMovementBounds);
+ mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
+ mMotionHelper.onMovementBoundsChanged();
boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
mPipBoundsState.setMinEdgeSize(
@@ -893,8 +888,10 @@
if (!mEnableResize) {
return false;
}
- return mExpandedBounds.width() != mNormalBounds.width()
- || mExpandedBounds.height() != mNormalBounds.height();
+ return mPipBoundsState.getExpandedBounds().width()
+ != mPipBoundsState.getNormalBounds().width()
+ || mPipBoundsState.getExpandedBounds().height()
+ != mPipBoundsState.getNormalBounds().height();
}
/**
@@ -910,11 +907,6 @@
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
- pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
- pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
- pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
- pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mMenuState=" + mMenuState);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index 75f01c5..d4acae5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -52,7 +52,7 @@
@After
open fun tearDown() {
- Assume.assumeTrue(isTelevision)
+ if (!isTelevision) return
testApp.forceStop()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
new file mode 100644
index 0000000..9ab0f89
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+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;
+
+/** Tests for {@link AppPair} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairTests {
+
+ private AppPairsController mController;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new TestAppPairsController(mTaskOrganizer, mSyncQueue);
+ }
+
+ @After
+ public void tearDown() {}
+
+ @Test
+ public void testContains() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ pair.unpair();
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ }
+
+ @Test
+ public void testVanishUnpairs() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ pair.onTaskVanished(task1);
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
new file mode 100644
index 0000000..ed85b67
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+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;
+
+/** Tests for {@link AppPairsController} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairsControllerTests {
+ private TestAppPairsController mController;
+ private TestAppPairsPool mPool;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new TestAppPairsController(mTaskOrganizer, mSyncQueue);
+ mPool = mController.getPool();
+ }
+
+ @After
+ public void tearDown() {}
+
+ @Test
+ public void testPairUnpair() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+
+ mController.unpair(task2.taskId);
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ assertThat(mPool.poolSize()).isGreaterThan(1);
+ }
+
+ @Test
+ public void testUnpair_DontReleaseToPool() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ mController.unpair(task2.taskId, false /* releaseToPool */);
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ assertThat(mPool.poolSize()).isEqualTo(1);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
new file mode 100644
index 0000000..4a0fe0f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+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;
+
+/** Tests for {@link AppPairsPool} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairsPoolTests {
+ private TestAppPairsController mController;
+ private TestAppPairsPool mPool;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new TestAppPairsController(mTaskOrganizer, mSyncQueue);
+ mPool = mController.getPool();
+ }
+
+ @After
+ public void tearDown() {}
+
+ @Test
+ public void testInitialState() {
+ // Pool should always start off with at least 1 entry.
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+ }
+
+ @Test
+ public void testAcquireRelease() {
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+ final AppPair appPair = mPool.acquire();
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+ mPool.release(appPair);
+ assertThat(mPool.poolSize()).isGreaterThan(1);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
new file mode 100644
index 0000000..7ea5a1b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+public class TestAppPairsController extends AppPairsController {
+ TestAppPairsPool mPool;
+
+ public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
+ super(organizer, syncQueue);
+ mPool = new TestAppPairsPool(this);
+ setPairsPool(mPool);
+ }
+
+ TestAppPairsPool getPool() {
+ return mPool;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java
new file mode 100644
index 0000000..080f207
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import android.app.ActivityManager;
+
+public class TestAppPairsPool extends AppPairsPool{
+ TestAppPairsPool(AppPairsController controller) {
+ super(controller);
+ }
+
+ @Override
+ void incrementPool() {
+ final AppPair entry = new AppPair(mController);
+ final ActivityManager.RunningTaskInfo info =
+ new TestRunningTaskInfoBuilder().build();
+ entry.onTaskAppeared(info, null /* leash */);
+ release(entry);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java
new file mode 100644
index 0000000..76d3a6a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.apppairs;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.window.IWindowContainerToken;
+import android.window.WindowContainerToken;
+
+public class TestRunningTaskInfoBuilder {
+ static int sNextTaskId = 500;
+ private Rect mBounds = new Rect(0, 0, 100, 100);
+ private WindowContainerToken mToken =
+ new WindowContainerToken(new IWindowContainerToken.Default());
+
+ TestRunningTaskInfoBuilder setBounds(Rect bounds) {
+ mBounds.set(bounds);
+ return this;
+ }
+
+ ActivityManager.RunningTaskInfo build() {
+ final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+ info.taskId = sNextTaskId++;
+ info.configuration.windowConfiguration.setBounds(mBounds);
+ info.token = mToken;
+ info.isResizeable = true;
+ return info;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index ec02331..b25c74d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -120,7 +120,7 @@
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
- assertEquals(expectedMinMovementBounds, mPipTouchHandler.mNormalMovementBounds);
+ assertEquals(expectedMinMovementBounds, mPipBoundsState.getNormalMovementBounds());
verify(mPipResizeGestureHandler, times(1))
.updateMinSize(mMinBounds.width(), mMinBounds.height());
}
@@ -139,7 +139,7 @@
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
- assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mExpandedMovementBounds);
+ assertEquals(expectedMaxMovementBounds, mPipBoundsState.getExpandedMovementBounds());
verify(mPipResizeGestureHandler, times(1))
.updateMaxSize(maxBounds.width(), maxBounds.height());
}
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index efcb373..f9df2f7 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -50,7 +50,11 @@
DrawPath,
DrawLine,
DrawVertices,
-
+ DrawImage,
+ DrawImageRect,
+ // DrawImageLattice also used to draw 9 patches
+ DrawImageLattice,
+ DrawPicture,
// TODO: Rest
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 6a76539..8c7113d 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -21,12 +21,16 @@
#include <SkPath.h>
#include <SkRegion.h>
#include <SkVertices.h>
+#include <SkImage.h>
+#include <SkPicture.h>
+#include <hwui/Bitmap.h>
#include <log/log.h>
#include "CanvasProperty.h"
#include "CanvasOpTypes.h"
#include <experimental/type_traits>
+#include <utility>
namespace android::uirenderer {
@@ -269,6 +273,97 @@
ASSERT_DRAWABLE()
};
+template<>
+struct CanvasOp<CanvasOpType::DrawImage> {
+
+ CanvasOp<CanvasOpType::DrawImageRect>(
+ const sk_sp<Bitmap>& bitmap,
+ float left,
+ float top,
+ SkPaint paint
+ ) : left(left),
+ top(top),
+ paint(std::move(paint)),
+ bitmap(bitmap),
+ image(bitmap->makeImage()) { }
+
+ float left;
+ float top;
+ SkPaint paint;
+ sk_sp<Bitmap> bitmap;
+ sk_sp<SkImage> image;
+
+ void draw(SkCanvas* canvas) const {
+ canvas->drawImage(image, left, top, &paint);
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
+struct CanvasOp<CanvasOpType::DrawImageRect> {
+
+ CanvasOp<CanvasOpType::DrawImageRect>(
+ const sk_sp<Bitmap>& bitmap,
+ SkRect src,
+ SkRect dst,
+ SkPaint paint
+ ) : src(src),
+ dst(dst),
+ paint(std::move(paint)),
+ bitmap(bitmap),
+ image(bitmap->makeImage()) { }
+
+ SkRect src;
+ SkRect dst;
+ SkPaint paint;
+ sk_sp<Bitmap> bitmap;
+ sk_sp<SkImage> image;
+
+ void draw(SkCanvas* canvas) const {
+ canvas->drawImageRect(image,
+ src,
+ dst,
+ &paint,
+ SkCanvas::kFast_SrcRectConstraint
+ );
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
+struct CanvasOp<CanvasOpType::DrawImageLattice> {
+
+ CanvasOp<CanvasOpType::DrawImageLattice>(
+ const sk_sp<Bitmap>& bitmap,
+ SkRect dst,
+ SkCanvas::Lattice lattice,
+ SkPaint paint
+ ): dst(dst),
+ lattice(lattice),
+ bitmap(bitmap),
+ image(bitmap->makeImage()),
+ paint(std::move(paint)) {}
+
+ SkRect dst;
+ SkCanvas::Lattice lattice;
+ const sk_sp<Bitmap> bitmap;
+ const sk_sp<SkImage> image;
+
+ SkPaint paint;
+ void draw(SkCanvas* canvas) const {
+ canvas->drawImageLattice(image.get(), lattice, dst, &paint);
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
+struct CanvasOp<CanvasOpType::DrawPicture> {
+ sk_sp<SkPicture> picture;
+ void draw(SkCanvas* canvas) const {
+ picture->playback(canvas);
+ }
+};
+
// cleanup our macros
#undef ASSERT_DRAWABLE
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index dc066da..b88ffa6 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -217,6 +217,20 @@
return result;
}
+
+static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName,
+ jobject typeface) {
+ ScopedUtfChars fieldNameChars(env, fieldName);
+ jfieldID fid =
+ env->GetStaticFieldID(cls, fieldNameChars.c_str(), "Landroid/graphics/Typeface;");
+ if (fid == 0) {
+ jniThrowRuntimeException(env, "Unable to find field");
+ return;
+ }
+ env->SetStaticObjectField(cls, fid, typeface);
+}
+
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gTypefaceMethods[] = {
@@ -237,6 +251,8 @@
(void*)Typeface_registerGenericFamily },
{ "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
{ "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
+ (void*)Typeface_forceSetStaticFinalField },
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index e7473c0..b15c322 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -22,7 +22,9 @@
#include <tests/common/CallCountingCanvas.h>
+#include "SkPictureRecorder.h"
#include "SkColor.h"
+#include "SkLatticeIter.h"
#include "pipeline/skia/AnimatedDrawables.h"
using namespace android;
@@ -198,7 +200,7 @@
TEST(CanvasOp, simpleDrawPaint) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawColor> {
+ buffer.push<Op::DrawColor> ({
.color = SkColor4f{1, 1, 1, 1},
.mode = SkBlendMode::kSrcIn
});
@@ -213,7 +215,7 @@
TEST(CanvasOp, simpleDrawPoint) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawPoint> {
+ buffer.push<Op::DrawPoint> ({
.x = 12,
.y = 42,
.paint = SkPaint{}
@@ -229,7 +231,7 @@
TEST(CanvasOp, simpleDrawLine) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawLine> {
+ buffer.push<Op::DrawLine> ({
.startX = 16,
.startY = 28,
.endX = 12,
@@ -247,7 +249,7 @@
TEST(CanvasOp, simpleDrawRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawRect> {
+ buffer.push<Op::DrawRect> ({
.paint = SkPaint{},
.rect = SkRect::MakeEmpty()
});
@@ -264,7 +266,7 @@
EXPECT_EQ(buffer.size(), 0);
SkRegion region;
region.setRect(SkIRect::MakeWH(12, 50));
- buffer.push(CanvasOp<Op::DrawRegion> {
+ buffer.push<Op::DrawRegion> ({
.paint = SkPaint{},
.region = region
});
@@ -286,7 +288,7 @@
clip.setRect(SkIRect::MakeWH(100, 100));
SkRegion region;
region.setPath(path, clip);
- buffer.push(CanvasOp<Op::DrawRegion> {
+ buffer.push<Op::DrawRegion> ({
.paint = SkPaint{},
.region = region
});
@@ -301,7 +303,7 @@
TEST(CanvasOp, simpleDrawRoundRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawRoundRect> {
+ buffer.push<Op::DrawRoundRect> ({
.paint = SkPaint{},
.rect = SkRect::MakeEmpty(),
.rx = 10,
@@ -340,7 +342,7 @@
innerPts[3].set(10, 10);
innerRRect.setRectRadii(inner, innerPts.get());
- buffer.push(CanvasOp<Op::DrawDoubleRoundRect> {
+ buffer.push<Op::DrawDoubleRoundRect> ({
.outer = outerRRect,
.inner = innerRRect,
.paint = SkPaint{}
@@ -356,7 +358,7 @@
TEST(CanvasOp, simpleDrawCircle) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawCircle> {
+ buffer.push<Op::DrawCircle>({
.cx = 5,
.cy = 7,
.radius = 10,
@@ -373,7 +375,7 @@
TEST(CanvasOp, simpleDrawOval) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawOval> {
+ buffer.push<Op::DrawOval> ({
.oval = SkRect::MakeEmpty(),
.paint = SkPaint{}
});
@@ -388,7 +390,7 @@
TEST(CanvasOp, simpleDrawArc) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawArc> {
+ buffer.push<Op::DrawArc>({
.oval = SkRect::MakeWH(100, 100),
.startAngle = 120,
.sweepAngle = 70,
@@ -408,7 +410,7 @@
EXPECT_EQ(buffer.size(), 0);
SkPath path;
path.addCircle(50, 50, 30);
- buffer.push(CanvasOp<Op::DrawPath> {
+ buffer.push<Op::DrawPath> ({
.path = path,
.paint = SkPaint{}
});
@@ -433,7 +435,7 @@
auto propertyPaint =
sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
- buffer.push(CanvasOp<Op::DrawRoundRectProperty> {
+ buffer.push<Op::DrawRoundRectProperty> ({
.left = left,
.top = top,
.right = right,
@@ -460,7 +462,7 @@
auto propertyPaint =
sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
- buffer.push(CanvasOp<Op::DrawCircleProperty> {
+ buffer.push<Op::DrawCircleProperty> ({
.x = x,
.y = y,
.radius = radius,
@@ -482,7 +484,7 @@
SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN};
sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 3, pts,
nullptr, colors);
- buffer.push(CanvasOp<Op::DrawVertices> {
+ buffer.push<Op::DrawVertices> ({
.vertices = vertices,
.mode = SkBlendMode::kSrcOver,
.paint = SkPaint{}
@@ -495,6 +497,110 @@
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
+TEST(CanvasOp, simpleDrawImage) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkImageInfo info =SkImageInfo::Make(5, 1,
+ kGray_8_SkColorType, kOpaque_SkAlphaType);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ buffer.push<Op::DrawImage> ({
+ bitmap,
+ 7,
+ 19,
+ SkPaint{}
+ }
+ );
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawImageCount);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, simpleDrawImageRect) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkImageInfo info = SkImageInfo::Make(5, 1,
+ kGray_8_SkColorType, kOpaque_SkAlphaType);
+
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ buffer.push<Op::DrawImageRect> ({
+ bitmap, SkRect::MakeWH(100, 100),
+ SkRect::MakeLTRB(120, 110, 220, 210),
+ SkPaint{}
+ }
+ );
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawImageRectCount);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, simpleDrawImageLattice) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkBitmap skBitmap;
+ skBitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));
+
+ const int xDivs[] = { 20, 50 };
+ const int yDivs[] = { 10, 40 };
+ SkCanvas::Lattice::RectType fillTypes[3][3];
+ memset(fillTypes, 0, sizeof(fillTypes));
+ fillTypes[1][1] = SkCanvas::Lattice::kTransparent;
+ SkColor colors[9];
+ SkCanvas::Lattice lattice = { xDivs, yDivs, fillTypes[0], 2,
+ 2, nullptr, colors };
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(&skBitmap);
+ buffer.push<Op::DrawImageLattice>(
+ {
+ bitmap,
+ SkRect::MakeWH(5, 1),
+ lattice,
+ SkPaint{}
+ }
+ );
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawImageLatticeCount);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, simpleDrawPicture) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkPictureRecorder recorder;
+ SkCanvas* pictureCanvas = recorder.beginRecording({64, 64, 192, 192});
+ SkPaint paint;
+ pictureCanvas->drawRect(SkRect::MakeWH(200, 200), paint);
+ paint.setColor(SK_ColorWHITE);
+ pictureCanvas->drawRect(SkRect::MakeLTRB(20, 20, 180, 180), paint);
+ sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
+ buffer.push<Op::DrawPicture> ({
+ .picture = picture
+ });
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ // Note because we are explicitly issuing 2 drawRect calls
+ // in the picture recorder above, when it is played back into
+ // CallCountingCanvas we will see 2 calls to drawRect instead of 1
+ // call to drawPicture.
+ // This is because SkiaCanvas::drawPicture uses picture.playback(canvas)
+ // instead of canvas->drawPicture.
+ EXPECT_EQ(2, canvas.drawRectCount);
+ EXPECT_EQ(2, canvas.sumTotalDrawCalls());
+}
+
TEST(CanvasOp, immediateRendering) {
auto canvas = std::make_shared<CallCountingCanvas>();
diff --git a/media/java/android/media/ApplicationMediaCapabilities.java b/media/java/android/media/ApplicationMediaCapabilities.java
index 4b28553..792e85f 100644
--- a/media/java/android/media/ApplicationMediaCapabilities.java
+++ b/media/java/android/media/ApplicationMediaCapabilities.java
@@ -23,11 +23,16 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* ApplicationMediaCapabilities is an immutable class that encapsulates an application's
- * capabilities of handling advanced media formats.
+ * capabilities for handling newer video codec format and media features.
*
- * The ApplicationMediaCapabilities class is used by the platform to to represent an application's
+ * The ApplicationMediaCapabilities class is used by the platform 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).
*
@@ -37,43 +42,55 @@
* 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
+ * <h3> Video Codec Support</h3>
+ * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
+ * for newer format with this class as they are assumed to support older format like h.264.
*
- * @hide
+ * <h4>Capability of handling HDR(high dynamic range) video</h4>
+ * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
+ * application will only need to specify individual types they supported.
+ *
+ * <h4>Capability of handling Slow Motion video</h4>
+ * There is no standard format for slow motion yet. If an application indicates support for slow
+ * motion, it is application's responsibility to parse the slow motion videos using their own parser
+ * or using support library.
*/
+// 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
+// TODO(hkuang): Add the support library page on parsing slow motion video.
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
- /** Whether handling of HEVC video is supported. */
- private final boolean mIsHevcSupported;
+ /** List of supported video codec mime types. */
+ // TODO: init it with avc and mpeg4 as application is assuming to support them.
+ private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
- /** Whether handling of slow-motion video is supported. */
- private final boolean mIsSlowMotionSupported;
+ /** List of supported hdr types. */
+ private Set<String> mSupportedHdrTypes = new HashSet<>();
- /** Whether handling of high dynamic range video is supported. */
- private final boolean mIsHdrSupported;
+ private boolean mIsSlowMotionSupported = false;
private ApplicationMediaCapabilities(Builder b) {
- mIsHevcSupported = b.mIsHevcSupported;
- mIsHdrSupported = b.mIsHdrSupported;
+ mSupportedVideoMimeTypes.addAll(b.getSupportedVideoMimeTypes());
+ mSupportedHdrTypes.addAll(b.getSupportedHdrTypes());
mIsSlowMotionSupported = b.mIsSlowMotionSupported;
}
- /** Whether handling of HEVC video is supported. */
- public boolean isHevcSupported() {
- return mIsHevcSupported;
+ /**
+ * Query if an video codec is supported by the application.
+ */
+ public boolean isVideoMimeTypeSupported(
+ @NonNull String videoMime) {
+ return mSupportedVideoMimeTypes.contains(videoMime);
}
- /** 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;
+ /**
+ * Query if a hdr type is supported by the application.
+ */
+ public boolean isHdrTypeSupported(
+ @NonNull @MediaFeature.MediaHdrType String hdrType) {
+ return mSupportedHdrTypes.contains(hdrType);
}
@Override
@@ -82,12 +99,86 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeBoolean(mIsHevcSupported);
- dest.writeBoolean(mIsHdrSupported);
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // Write out the supported video mime types.
+ dest.writeInt(mSupportedVideoMimeTypes.size());
+ for (String cap : mSupportedVideoMimeTypes) {
+ dest.writeString(cap);
+ }
+ // Write out the supported hdr types.
+ dest.writeInt(mSupportedHdrTypes.size());
+ for (String cap : mSupportedHdrTypes) {
+ dest.writeString(cap);
+ }
+ // Write out the supported slow motion.
dest.writeBoolean(mIsSlowMotionSupported);
}
+ @Override
+ public String toString() {
+ String caps = new String(
+ "Supported Video MimeTypes: " + mSupportedVideoMimeTypes.toString());
+ caps += "Supported HDR types: " + mSupportedHdrTypes.toString();
+ caps += "Supported slow motion: " + mIsSlowMotionSupported;
+ return caps;
+ }
+
+ @NonNull
+ public static final Creator<ApplicationMediaCapabilities> CREATOR =
+ new Creator<ApplicationMediaCapabilities>() {
+ public ApplicationMediaCapabilities createFromParcel(Parcel in) {
+ ApplicationMediaCapabilities.Builder builder =
+ new ApplicationMediaCapabilities.Builder();
+
+ // Parse supported video codec mime types.
+ int count = in.readInt();
+ for (int readCount = 0; readCount < count; ++readCount) {
+ builder.addSupportedVideoMimeType(in.readString());
+ }
+ // Parse supported hdr types.
+ count = in.readInt();
+ for (int readCount = 0; readCount < count; ++readCount) {
+ builder.addSupportedHdrType(in.readString());
+ }
+
+ boolean supported = in.readBoolean();
+ builder.setSlowMotionSupported(supported);
+
+ return builder.build();
+ }
+
+ public ApplicationMediaCapabilities[] newArray(int size) {
+ return new ApplicationMediaCapabilities[size];
+ }
+ };
+
+ /*
+ * Returns a list that contains all the video codec mime types supported by the application.
+ * The list will be empty if no codecs are supported by the application.
+ * @return List of supported video codec mime types.
+ */
+ @NonNull
+ public List<String> getSupportedVideoMimeTypes() {
+ return new ArrayList<>(mSupportedVideoMimeTypes);
+ }
+
+ /*
+ * Returns a list that contains all hdr types supported by the application.
+ * The list will be empty if no hdr types are supported by the application.
+ * @return List of supported hdr types.
+ */
+ @NonNull
+ public List<String> getSupportedHdrTypes() {
+ return new ArrayList<>(mSupportedHdrTypes);
+ }
+
+ /*
+ * Whether handling of slow-motion video is supported
+ */
+ public boolean isSlowMotionSupported() {
+ return mIsSlowMotionSupported;
+ }
+
/**
* Builder class for {@link ApplicationMediaCapabilities} objects.
* Use this class to configure and create an ApplicationMediaCapabilities instance. Builder
@@ -96,8 +187,12 @@
* //TODO(hkuang): Add xml parsing support to the builder.
*/
public final static class Builder {
- private boolean mIsHevcSupported = false;
- private boolean mIsHdrSupported = false;
+ /** List of supported video codec mime types. */
+ private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
+
+ /** List of supported hdr types. */
+ private Set<String> mSupportedHdrTypes = new HashSet<>();
+
private boolean mIsSlowMotionSupported = false;
/**
@@ -112,37 +207,62 @@
* @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.
+ * <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.");
+ // If hdr is supported, application must also support hevc.
+ if (!mSupportedHdrTypes.isEmpty() && !mSupportedVideoMimeTypes.contains(
+ MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+ throw new UnsupportedOperationException("Only support HEVC mime type");
}
return new ApplicationMediaCapabilities(this);
}
/**
- * Sets whether supports HEVC encoded video.
+ * Adds a supported video codec mime type.
+ *
+ * @param codecMime Supported codec mime types. Must be one of the mime type defined
+ * in {@link MediaFormat}.
+ * @throws UnsupportedOperationException if the codec mime type is not supported.
+ * @throws IllegalArgumentException if mime type is not valid.
*/
@NonNull
- public Builder setHevcSupported(boolean hevcSupported) {
- mIsHevcSupported = hevcSupported;
+ public Builder addSupportedVideoMimeType(
+ @NonNull String codecMime) {
+ mSupportedVideoMimeTypes.add(codecMime);
return this;
}
- /**
- * Sets whether supports high dynamic range video.
- */
- @NonNull
- public Builder setHdrSupported(boolean hdrSupported) {
- mIsHdrSupported = hdrSupported;
- return this;
+ private List<String> getSupportedVideoMimeTypes() {
+ return new ArrayList<>(mSupportedVideoMimeTypes);
}
/**
- * Sets whether supports slow-motion video.
+ * Adds a supported hdr type.
+ *
+ * @param hdrType Supported hdr types. Must be one of the String defined in
+ * {@link MediaFeature.HdrType}.
+ * @throws IllegalArgumentException if hdrType is not valid.
+ */
+ @NonNull
+ public Builder addSupportedHdrType(
+ @NonNull @MediaFeature.MediaHdrType String hdrType) {
+ mSupportedHdrTypes.add(hdrType);
+ return this;
+ }
+
+ private List<String> getSupportedHdrTypes() {
+ return new ArrayList<>(mSupportedHdrTypes);
+ }
+
+ /**
+ * Sets whether slow-motion video is supported.
+ * If an application indicates support for slow-motion, it is application's responsibility
+ * to parse the slow-motion videos using their own parser or using support library.
+ * @see android.media.MediaFormat#KEY_SLOW_MOTION_MARKERS
*/
@NonNull
public Builder setSlowMotionSupported(boolean slowMotionSupported) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2947736..4578883 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4009,8 +4009,8 @@
* @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
*/
@SystemApi
- @SuppressLint("Doclava125") // no permission enforcement, but only "undoes" what would have been
- // done by a matching requestAudioFocus
+ @SuppressLint("RequiresPermission") // no permission enforcement, but only "undoes" what would
+ // have been done by a matching requestAudioFocus
public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
int status = AUDIOFOCUS_REQUEST_FAILED;
unregisterAudioFocusRequest(l);
@@ -5602,7 +5602,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125") // FIXME is this still used?
+ @SuppressLint("RequiresPermission") // FIXME is this still used?
public boolean isHdmiSystemAudioSupported() {
try {
return getService().isHdmiSystemAudioSupported();
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 610bffe..0ef4b94 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -200,6 +200,7 @@
* @return The window transformation that needs to be applied for this frame.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract int getTransform();
/**
@@ -207,6 +208,7 @@
* @return The scaling mode that needs to be applied for this frame.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract int getScalingMode();
/**
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 5c1f893..9957975 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.app.ActivityManager;
import android.content.Context;
import android.hardware.cas.V1_0.HidlCasPluginDescriptor;
import android.hardware.cas.V1_0.ICas;
@@ -679,7 +678,7 @@
private void createPlugin(int casSystemId) throws UnsupportedCasException {
try {
mCasSystemId = casSystemId;
- mUserId = ActivityManager.getCurrentUser();
+ mUserId = Process.myUid();
IMediaCasService service = getService();
android.hardware.cas.V1_2.IMediaCasService serviceV12 =
android.hardware.cas.V1_2.IMediaCasService.castFrom(service);
diff --git a/media/java/android/media/MediaFeature.java b/media/java/android/media/MediaFeature.java
new file mode 100644
index 0000000..0e461888
--- /dev/null
+++ b/media/java/android/media/MediaFeature.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * MediaFeature defines various media features, e.g. hdr type.
+ */
+public final class MediaFeature {
+ /**
+ * Defines tye type of HDR(high dynamic range) video.
+ */
+ public static final class HdrType {
+ private HdrType() {
+ }
+
+ /**
+ * HDR type for dolby-vision.
+ */
+ public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
+ /**
+ * HDR type for hdr10.
+ */
+ public static final String HDR10 = "android.media.feature.hdr.hdr10";
+ /**
+ * HDR type for hdr10+.
+ */
+ public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
+ /**
+ * HDR type for hlg.
+ */
+ public static final String HLG = "android.media.feature.hdr.hlg";
+ }
+
+ /** @hide */
+ @StringDef({
+ HdrType.DOLBY_VISION,
+ HdrType.HDR10,
+ HdrType.HDR10_PLUS,
+ HdrType.HLG,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaHdrType {
+ }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 97161e5..27b33ac 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -740,10 +740,14 @@
@Result
public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
- if (mScanCallback != null || mScanCallbackExecutor != null) {
+ /**
+ * Scan can be called again for blink scan if scanCallback and executor are same as before.
+ */
+ if (((mScanCallback != null) && (mScanCallback != scanCallback))
+ || ((mScanCallbackExecutor != null) && (mScanCallbackExecutor != executor))) {
throw new IllegalStateException(
- "Scan already in progress. stopScan must be called before a new scan can be "
- + "started.");
+ "Different Scan session already in progress. stopScan must be called "
+ + "before a new scan session can be " + "started.");
}
mFrontendType = settings.getType();
if (mFrontendType == FrontendSettings.TYPE_DTMB) {
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java
index 8a668c6..3f3f49a 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java
@@ -34,6 +34,8 @@
import static org.testng.Assert.assertThrows;
import android.media.ApplicationMediaCapabilities;
+import android.media.MediaFeature;
+import android.media.MediaFormat;
import android.test.ActivityInstrumentationTestCase2;
import org.junit.Test;
@@ -49,37 +51,39 @@
@Test
public void testSetSupportHevc() throws Exception {
ApplicationMediaCapabilities capability =
- new ApplicationMediaCapabilities.Builder().setHevcSupported(true).build();
- assertTrue(capability.isHevcSupported());
+ new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).build();
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
ApplicationMediaCapabilities capability2 =
- new ApplicationMediaCapabilities.Builder().setHevcSupported(false).build();
- assertFalse(capability2.isHevcSupported());
+ new ApplicationMediaCapabilities.Builder().build();
+ assertFalse(capability2.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
}
@Test
public void testSetSupportHdr() throws Exception {
ApplicationMediaCapabilities capability =
- new ApplicationMediaCapabilities.Builder().setHdrSupported(true).setHevcSupported(
- true).build();
- assertEquals(true, capability.isHdrSupported());
+ new ApplicationMediaCapabilities.Builder().addSupportedHdrType(
+ MediaFeature.HdrType.HDR10_PLUS).addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).build();
+ assertEquals(true, capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS));
}
@Test
public void testSetSupportSlowMotion() throws Exception {
ApplicationMediaCapabilities capability =
- new ApplicationMediaCapabilities.Builder().setSlowMotionSupported(
- true).build();
+ 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());
+ new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
+ MediaFormat.MIMETYPE_VIDEO_HEVC).addSupportedHdrType(
+ MediaFeature.HdrType.HDR10_PLUS).setSlowMotionSupported(true).build();
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS));
assertTrue(capability.isSlowMotionSupported());
}
@@ -87,8 +91,8 @@
public void testSupportHdrWithoutSupportHevc() throws Exception {
assertThrows(UnsupportedOperationException.class, () -> {
ApplicationMediaCapabilities capability =
- new ApplicationMediaCapabilities.Builder().setHdrSupported(
- true).setHevcSupported(false).build();
+ new ApplicationMediaCapabilities.Builder().addSupportedHdrType(
+ MediaFeature.HdrType.HDR10_PLUS).build();
});
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 20b4451..a97af4b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -249,6 +249,7 @@
<!-- Permission required for CTS test - UiModeManagerTest -->
<uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
+ <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
<!-- Permission required for CTS tests - UiModeManagerTest, CarModeInCallServiceTest -->
<uses-permission android:name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION"/>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java
index fac9e98..6799450 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java
@@ -14,6 +14,8 @@
package com.android.systemui.plugins;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -30,13 +32,32 @@
void onListenerConnected(NotificationProvider provider);
+ /**
+ * @return whether plugin wants to skip the default callbacks.
+ */
default boolean onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
return false;
}
+
+ /**
+ * @return whether plugin wants to skip the default callbacks.
+ */
default boolean onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
return false;
}
+ /**
+ * Called when a notification channel is modified.
+ * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
+ * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
+ * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
+ * @return whether a plugin wants to skip the default callbacks.
+ */
+ default boolean onNotificationChannelModified(
+ String pkgName, UserHandle user, NotificationChannel channel, int modificationType) {
+ return false;
+ }
+
default StatusBarNotification[] getActiveNotifications(
StatusBarNotification[] activeNotifications) {
return activeNotifications;
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 2439fed..aed067c 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -88,7 +88,8 @@
android:layout_width="8dp"
android:layout_height="match_parent"
android:visibility="visible" />
- <!-- Negative Button -->
+
+ <!-- Negative Button, reserved for app -->
<Button android:id="@+id/button_negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -97,13 +98,32 @@
android:ellipsize="end"
android:maxLines="2"
android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"/>
+ <!-- Cancel Button, replaces negative button when biometric is accepted -->
+ <Button android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_gravity="center_vertical"
+ android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
+ android:text="@string/cancel"
+ android:visibility="gone"/>
+ <!-- "Use Credential" Button, replaces if device credential is allowed -->
+ <Button android:id="@+id/button_use_credential"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_gravity="center_vertical"
+ android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
+ android:visibility="gone"/>
+
<Space android:id="@+id/middleSpacer"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="visible"/>
+
<!-- Positive Button -->
- <Button android:id="@+id/button_positive"
+ <Button android:id="@+id/button_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@*android:style/Widget.DeviceDefault.Button.Colored"
@@ -124,6 +144,7 @@
android:maxWidth="@dimen/biometric_dialog_button_positive_max_width"
android:text="@string/biometric_dialog_try_again"
android:visibility="gone"/>
+
<Space android:id="@+id/rightSpacer"
android:layout_width="8dp"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 4527c6c..89bf12d 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -29,25 +29,6 @@
android:elevation="4dp"
android:background="@drawable/qs_background_primary" />
- <!-- Black part behind the status bar -->
- <View
- android:id="@+id/quick_settings_status_bar_background"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/quick_qs_offset_height"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:background="#ff000000" />
-
- <!-- Gradient view behind QS -->
- <View
- android:id="@+id/quick_settings_gradient_view"
- android:layout_width="match_parent"
- android:layout_height="126dp"
- android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:background="@drawable/qs_bg_gradient" />
-
<com.android.systemui.qs.NonInterceptingScrollView
android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 3c74801..f663ab4e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -43,8 +43,7 @@
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- systemui:showDark="false" />
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
</LinearLayout>
<android.widget.Space
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 31a6e1e..92e0c1d 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -86,16 +86,11 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"বেশি জায়গা নেই তাই স্ক্রিনশটটি সেভ করা যাবে না৷"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string>
- <!-- no translation found for screenshot_edit_label (8754981973544133050) -->
- <skip />
- <!-- no translation found for screenshot_edit_description (3333092254706788906) -->
- <skip />
- <!-- no translation found for screenshot_scroll_label (7682877978685434621) -->
- <skip />
- <!-- no translation found for screenshot_scroll_description (7855773867093272175) -->
- <skip />
- <!-- no translation found for screenshot_dismiss_description (4702341245899508786) -->
- <skip />
+ <string name="screenshot_edit_label" msgid="8754981973544133050">"এডিট করুন"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"স্ক্রিনশট এডিট করুন"</string>
+ <string name="screenshot_scroll_label" msgid="7682877978685434621">"স্ক্রল করুন"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"স্ক্রিনশট স্ক্রল করুন"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"স্ক্রিনশট বাতিল করুন"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রিন রেকর্ডিং প্রসেস হচ্ছে"</string>
diff --git a/packages/SystemUI/res/values-bn/strings_tv.xml b/packages/SystemUI/res/values-bn/strings_tv.xml
index f9da81a..1c26840 100644
--- a/packages/SystemUI/res/values-bn/strings_tv.xml
+++ b/packages/SystemUI/res/values-bn/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"মাইক্রোফোন চালু আছে"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s আপনার ডিভাইসের মাইক্রোফোন অ্যাক্সেস করেছে"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN কানেক্ট করা হয়েছে"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ডিসকানেক্ট করা হয়েছে"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-এর মাধ্যমে"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings_tv.xml b/packages/SystemUI/res/values-de/strings_tv.xml
index 8756749..34fc6aa 100644
--- a/packages/SystemUI/res/values-de/strings_tv.xml
+++ b/packages/SystemUI/res/values-de/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"Mikrofon aktiv"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s hat auf dein Mikrofon zugegriffen"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ist verbunden"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ist nicht verbunden"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Über <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index d9b805b..2ce2718 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -88,8 +88,8 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Editar"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Editar captura de pantalla"</string>
- <string name="screenshot_scroll_label" msgid="7682877978685434621">"Desplazarse"</string>
- <string name="screenshot_scroll_description" msgid="7855773867093272175">"Desplazarse por la captura de pantalla"</string>
+ <string name="screenshot_scroll_label" msgid="7682877978685434621">"Pantalla continua"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Hacer captura de pantalla continua"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Cerrar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 453d32e..835449f 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -89,7 +89,7 @@
<string name="screenshot_edit_label" msgid="8754981973544133050">"Editatu"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Editatu pantaila-argazkia"</string>
<string name="screenshot_scroll_label" msgid="7682877978685434621">"Egin gora eta behera"</string>
- <string name="screenshot_scroll_description" msgid="7855773867093272175">"Pantaila-kaptura etengabea"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Pantaila-argazki etengabea"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Baztertu pantaila-argazkia"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
diff --git a/packages/SystemUI/res/values-gu/strings_tv.xml b/packages/SystemUI/res/values-gu/strings_tv.xml
index 09346fa..297b6e1a 100644
--- a/packages/SystemUI/res/values-gu/strings_tv.xml
+++ b/packages/SystemUI/res/values-gu/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"માઇક્રોફોન સક્રિય છે"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$sએ તમારો માઇક્રોફોન ઍક્સેસ કર્યો હતો"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN કનેક્ટ કરેલું છે"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ડિસ્કનેક્ટ કરેલું છે"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> મારફતે"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index ff67a40..a487a63 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -86,16 +86,11 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
- <!-- no translation found for screenshot_edit_label (8754981973544133050) -->
- <skip />
- <!-- no translation found for screenshot_edit_description (3333092254706788906) -->
- <skip />
- <!-- no translation found for screenshot_scroll_label (7682877978685434621) -->
- <skip />
- <!-- no translation found for screenshot_scroll_description (7855773867093272175) -->
- <skip />
- <!-- no translation found for screenshot_dismiss_description (4702341245899508786) -->
- <skip />
+ <string name="screenshot_edit_label" msgid="8754981973544133050">"संपादित करा"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रीनशॉट संपादित करा"</string>
+ <string name="screenshot_scroll_label" msgid="7682877978685434621">"स्क्रोल करा"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रीनशॉटवर स्क्रोल करा"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रीनशॉट डिसमिस करा"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string>
diff --git a/packages/SystemUI/res/values-mr/strings_tv.xml b/packages/SystemUI/res/values-mr/strings_tv.xml
index 0d0d8e1..791a4fd 100644
--- a/packages/SystemUI/res/values-mr/strings_tv.xml
+++ b/packages/SystemUI/res/values-mr/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"मायक्रोफोन ॲक्टिव्ह आहे"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s यांनी तुमचा मायक्रोफोन अॅक्सेस केला आहे"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN कनेक्ट केले"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN डिस्कनेक्ट केले"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> द्वारे"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 560fd0b..eca79cb 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -86,16 +86,11 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
- <!-- no translation found for screenshot_edit_label (8754981973544133050) -->
- <skip />
- <!-- no translation found for screenshot_edit_description (3333092254706788906) -->
- <skip />
- <!-- no translation found for screenshot_scroll_label (7682877978685434621) -->
- <skip />
- <!-- no translation found for screenshot_scroll_description (7855773867093272175) -->
- <skip />
- <!-- no translation found for screenshot_dismiss_description (4702341245899508786) -->
- <skip />
+ <string name="screenshot_edit_label" msgid="8754981973544133050">"सम्पादन गर्नुहोस्"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रिनसट सम्पादन गर्नुहोस्"</string>
+ <string name="screenshot_scroll_label" msgid="7682877978685434621">"स्क्रोल गर्नुहोस्"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रिनसट स्क्रोल गर्नुहोस्"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रिनसट हटाउनुहोस्"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string>
diff --git a/packages/SystemUI/res/values-ne/strings_tv.xml b/packages/SystemUI/res/values-ne/strings_tv.xml
index 6f3eb66..00cc10a 100644
--- a/packages/SystemUI/res/values-ne/strings_tv.xml
+++ b/packages/SystemUI/res/values-ne/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"माइक्रोफोन सक्रिय छ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ले तपाईंको माइक्रोफोनमाथि पहुँच राख्यो"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN कनेक्ट गरिएको छ"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN डिस्कनेक्ट गरिएको छ"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> मार्फत"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index d3ebe52..297ebc6 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -86,16 +86,11 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Provo ta nxjerrësh përsëri pamjen e ekranit"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
- <!-- no translation found for screenshot_edit_label (8754981973544133050) -->
- <skip />
- <!-- no translation found for screenshot_edit_description (3333092254706788906) -->
- <skip />
- <!-- no translation found for screenshot_scroll_label (7682877978685434621) -->
- <skip />
- <!-- no translation found for screenshot_scroll_description (7855773867093272175) -->
- <skip />
- <!-- no translation found for screenshot_dismiss_description (4702341245899508786) -->
- <skip />
+ <string name="screenshot_edit_label" msgid="8754981973544133050">"Modifiko"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"Modifiko pamjen e ekranit"</string>
+ <string name="screenshot_scroll_label" msgid="7682877978685434621">"Lëviz"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Lëviz në pamjen e ekranit"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hiq pamjen e ekranit"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
diff --git a/packages/SystemUI/res/values-sq/strings_tv.xml b/packages/SystemUI/res/values-sq/strings_tv.xml
index 124f664..713130f 100644
--- a/packages/SystemUI/res/values-sq/strings_tv.xml
+++ b/packages/SystemUI/res/values-sq/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"Mikrofoni aktiv"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s pati qasje te mikrofoni yt"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-ja është e lidhur"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-ja është shkëputur"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Nëpërmjet <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 578f62b..5fed052 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -86,16 +86,11 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"స్క్రీన్షాట్ తీయడానికి మళ్లీ ప్రయత్నించండి"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"నిల్వ స్థలం పరిమితంగా ఉన్న కారణంగా స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడదు"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"స్క్రీన్షాట్లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string>
- <!-- no translation found for screenshot_edit_label (8754981973544133050) -->
- <skip />
- <!-- no translation found for screenshot_edit_description (3333092254706788906) -->
- <skip />
- <!-- no translation found for screenshot_scroll_label (7682877978685434621) -->
- <skip />
- <!-- no translation found for screenshot_scroll_description (7855773867093272175) -->
- <skip />
- <!-- no translation found for screenshot_dismiss_description (4702341245899508786) -->
- <skip />
+ <string name="screenshot_edit_label" msgid="8754981973544133050">"ఎడిట్ చేయండి"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"స్క్రీన్షాట్ను ఎడిట్ చేయండి"</string>
+ <string name="screenshot_scroll_label" msgid="7682877978685434621">"స్క్రోల్ చేయి"</string>
+ <string name="screenshot_scroll_description" msgid="7855773867093272175">"స్క్రీన్షాట్కు స్క్రోల్ చేయండి"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"స్క్రీన్షాట్ను విస్మరించు"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్షాట్ ప్రివ్యూ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
@@ -128,7 +123,7 @@
<string name="installer_cd_button_title" msgid="5499998592841984743">"Macకు Android ఫైల్ బదిలీ యాప్ ఇన్స్టాల్ చేయండి"</string>
<string name="accessibility_back" msgid="6530104400086152611">"వెనుకకు"</string>
<string name="accessibility_home" msgid="5430449841237966217">"హోమ్"</string>
- <string name="accessibility_menu" msgid="2701163794470513040">"మెను"</string>
+ <string name="accessibility_menu" msgid="2701163794470513040">"మెనూ"</string>
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"యాక్సెస్ సామర్థ్యం"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"స్క్రీన్ను తిప్పండి"</string>
<string name="accessibility_recent" msgid="901641734769533575">"ఓవర్వ్యూ"</string>
@@ -932,7 +927,7 @@
<string name="tuner_minus" msgid="5258518368944598545">"తీసివేత చిహ్నం"</string>
<string name="tuner_left" msgid="5758862558405684490">"ఎడమ"</string>
<string name="tuner_right" msgid="8247571132790812149">"కుడి"</string>
- <string name="tuner_menu" msgid="363690665924769420">"మెను"</string>
+ <string name="tuner_menu" msgid="363690665924769420">"మెనూ"</string>
<string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g> అనురవర్తనం"</string>
<string name="notification_channel_alerts" msgid="3385787053375150046">"హెచ్చరికలు"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"బ్యాటరీ"</string>
diff --git a/packages/SystemUI/res/values-te/strings_tv.xml b/packages/SystemUI/res/values-te/strings_tv.xml
index 112f9bc..67fb678 100644
--- a/packages/SystemUI/res/values-te/strings_tv.xml
+++ b/packages/SystemUI/res/values-te/strings_tv.xml
@@ -21,10 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mic_active" msgid="5766614241012047024">"మైక్రోఫోన్ యాక్టివ్గా ఉంది"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"మీ మైక్రోఫోన్ను %1$s యాక్సెస్ చేసింది"</string>
- <!-- no translation found for notification_vpn_connected (3891023882833274730) -->
- <skip />
- <!-- no translation found for notification_vpn_disconnected (7150747626448044843) -->
- <skip />
- <!-- no translation found for notification_disclosure_vpn_text (3873532735584866236) -->
- <skip />
+ <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN కనెక్ట్ చేయబడింది"</string>
+ <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN డిస్కనెక్ట్ చేయబడింది"</string>
+ <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా"</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/FontInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/FontInterpolator.kt
new file mode 100644
index 0000000..962c002
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/FontInterpolator.kt
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard
+
+import android.graphics.fonts.Font
+import android.graphics.fonts.FontVariationAxis
+import android.util.MathUtils
+
+private const val TAG_WGHT = "wght"
+private const val TAG_ITAL = "ital"
+
+private const val FONT_WEIGHT_MAX = 1000f
+private const val FONT_WEIGHT_MIN = 0f
+private const val FONT_WEIGHT_ANIMATION_STEP = 10f
+private const val FONT_WEIGHT_DEFAULT_VALUE = 400f
+
+private const val FONT_ITALIC_MAX = 1f
+private const val FONT_ITALIC_MIN = 0f
+private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
+private const val FONT_ITALIC_DEFAULT_VALUE = 0f
+
+/**
+ * Provide interpolation of two fonts by adjusting font variation settings.
+ */
+class FontInterpolator {
+
+ /**
+ * Cache key for the interpolated font.
+ *
+ * This class is mutable for recycling.
+ */
+ private data class InterpKey(var l: Font?, var r: Font?, var progress: Float) {
+ fun set(l: Font, r: Font, progress: Float) {
+ this.l = l
+ this.r = r
+ this.progress = progress
+ }
+ }
+
+ /**
+ * Cache key for the font that has variable font.
+ *
+ * This class is mutable for recycling.
+ */
+ private data class VarFontKey(
+ var sourceId: Int,
+ var index: Int,
+ val sortedAxes: MutableList<FontVariationAxis>
+ ) {
+ constructor(font: Font, axes: List<FontVariationAxis>):
+ this(font.sourceIdentifier,
+ font.ttcIndex,
+ axes.toMutableList().apply { sortBy { it.tag } }
+ )
+
+ fun set(font: Font, axes: List<FontVariationAxis>) {
+ sourceId = font.sourceIdentifier
+ index = font.ttcIndex
+ sortedAxes.clear()
+ sortedAxes.addAll(axes)
+ sortedAxes.sortBy { it.tag }
+ }
+ }
+
+ // Font interpolator has two level caches: one for input and one for font with different
+ // variation settings. No synchronization is needed since FontInterpolator is not designed to be
+ // thread-safe and can be used only on UI thread.
+ private val interpCache = hashMapOf<InterpKey, Font>()
+ private val verFontCache = hashMapOf<VarFontKey, Font>()
+
+ // Mutable keys for recycling.
+ private val tmpInterpKey = InterpKey(null, null, 0f)
+ private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf())
+
+ /**
+ * Linear interpolate the font variation settings.
+ */
+ fun lerp(start: Font, end: Font, progress: Float): Font {
+ if (progress == 0f) {
+ return start
+ } else if (progress == 1f) {
+ return end
+ }
+
+ val startAxes = start.axes ?: EMPTY_AXES
+ val endAxes = end.axes ?: EMPTY_AXES
+
+ if (startAxes.isEmpty() && endAxes.isEmpty()) {
+ return start
+ }
+
+ // Check we already know the result. This is commonly happens since we draws the different
+ // text chunks with the same font.
+ tmpInterpKey.set(start, end, progress)
+ val cachedFont = interpCache[tmpInterpKey]
+ if (cachedFont != null) {
+ return cachedFont
+ }
+
+ // General axes interpolation takes O(N log N), this is came from sorting the axes. Usually
+ // this doesn't take much time since the variation axes is usually up to 5. If we need to
+ // support more number of axes, we may want to preprocess the font and store the sorted axes
+ // and also pre-fill the missing axes value with default value from 'fvar' table.
+ val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue ->
+ when (tag) {
+ // TODO: Good to parse 'fvar' table for retrieving default value.
+ TAG_WGHT -> adjustWeight(
+ MathUtils.lerp(
+ startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
+ endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
+ progress))
+ TAG_ITAL -> adjustItalic(
+ MathUtils.lerp(
+ startValue ?: FONT_ITALIC_DEFAULT_VALUE,
+ endValue ?: FONT_ITALIC_DEFAULT_VALUE,
+ progress))
+ else -> {
+ require(startValue != null && endValue != null) {
+ "Unable to interpolate due to unknown default axes value : $tag"
+ }
+ MathUtils.lerp(startValue, endValue, progress)
+ }
+ }
+ }
+
+ // Check if we already make font for this axes. This is typically happens if the animation
+ // happens backward.
+ tmpVarFontKey.set(start, newAxes)
+ val axesCachedFont = verFontCache[tmpVarFontKey]
+ if (axesCachedFont != null) {
+ interpCache[InterpKey(start, end, progress)] = axesCachedFont
+ return axesCachedFont
+ }
+
+ // This is the first time to make the font for the axes. Build and store it to the cache.
+ // Font.Builder#build won't throw IOException since creating fonts from existing fonts will
+ // not do any IO work.
+ val newFont = Font.Builder(start)
+ .setFontVariationSettings(newAxes.toTypedArray())
+ .build()
+ interpCache[InterpKey(start, end, progress)] = newFont
+ verFontCache[VarFontKey(start, newAxes)] = newFont
+ return newFont
+ }
+
+ private fun lerp(
+ start: Array<FontVariationAxis>,
+ end: Array<FontVariationAxis>,
+ filter: (tag: String, left: Float?, right: Float?) -> Float
+ ): List<FontVariationAxis> {
+ // Safe to modify result of Font#getAxes since it returns cloned object.
+ start.sortBy { axis -> axis.tag }
+ end.sortBy { axis -> axis.tag }
+
+ val result = mutableListOf<FontVariationAxis>()
+ var i = 0
+ var j = 0
+ while (i < start.size || j < end.size) {
+ val tagA = if (i < start.size) start[i].tag else null
+ val tagB = if (j < end.size) end[j].tag else null
+
+ val comp = when {
+ tagA == null -> 1
+ tagB == null -> -1
+ else -> tagA.compareTo(tagB)
+ }
+
+ val axis = when {
+ comp == 0 -> {
+ val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
+ FontVariationAxis(tagA, v)
+ }
+ comp < 0 -> {
+ val v = filter(tagA!!, start[i++].styleValue, null)
+ FontVariationAxis(tagA, v)
+ }
+ else -> { // comp > 0
+ val v = filter(tagB!!, null, end[j++].styleValue)
+ FontVariationAxis(tagB, v)
+ }
+ }
+
+ result.add(axis)
+ }
+ return result
+ }
+
+ // For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
+ // Cache hit ratio in the Skia glyph cache.
+ private fun adjustWeight(value: Float) =
+ coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
+
+ // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
+ // Cache hit ratio in the Skia glyph cache.
+ private fun adjustItalic(value: Float) =
+ coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
+
+ private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
+ (v.coerceIn(min, max) / step).toInt() * step
+
+ companion object {
+ private val EMPTY_AXES = arrayOf<FontVariationAxis>()
+
+ // Returns true if given two font instance can be interpolated.
+ fun canInterpolate(start: Font, end: Font) =
+ start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
index 7cf1bd0..3942c60 100644
--- a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
+++ b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
@@ -16,12 +16,17 @@
package com.android.keyguard;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.widget.TextClock;
+import kotlin.Unit;
+
/**
* Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
* The time's text color is a gradient that changes its colors based on its controller.
@@ -30,6 +35,8 @@
private int[] mGradientColors;
private float[] mPositions;
+ private TextAnimator mTextAnimator = null;
+
public GradientTextClock(Context context) {
this(context, null, 0, 0);
}
@@ -74,6 +81,24 @@
super.setFormat24Hour(FORMAT_24);
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mTextAnimator == null) {
+ mTextAnimator = new TextAnimator(getLayout(), () -> {
+ invalidate();
+ return Unit.INSTANCE;
+ });
+ } else {
+ mTextAnimator.updateLayout(getLayout());
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mTextAnimator.draw(canvas);
+ }
+
public void setGradientColors(int[] colors) {
mGradientColors = colors;
updatePaint();
@@ -83,11 +108,33 @@
mPositions = positions;
}
+ /**
+ * Set text style with animation.
+ *
+ * By passing -1 to weight, the view preserve the current weight.
+ * By passing -1 to textSize, the view preserve the current text size.
+ *
+ * @param weight text weight.
+ * @param textSize font size.
+ * @param animate true for changing text style with animation, otherwise false.
+ */
+ public void setTextStyle(
+ @IntRange(from = 0, to = 1000) int weight,
+ @FloatRange(from = 0) float textSize,
+ boolean animate) {
+ if (mTextAnimator != null) {
+ mTextAnimator.setTextStyle(weight, textSize, animate, -1, null);
+ }
+ }
+
private void updatePaint() {
- getPaint().setShader(
- new LinearGradient(
- getX(), getY(), getX(), getMeasuredHeight() + getY(),
- mGradientColors, mPositions, Shader.TileMode.REPEAT));
+ Shader shader = new LinearGradient(
+ getX(), getY(), getX(), getMeasuredHeight() + getY(), mGradientColors, mPositions,
+ Shader.TileMode.REPEAT);
+ getPaint().setShader(shader);
+ if (mTextAnimator != null) {
+ mTextAnimator.setShader(shader);
+ }
}
private final OnLayoutChangeListener mOnLayoutChangeListener =
diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
new file mode 100644
index 0000000..e4c3dcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.TimeInterpolator
+import android.animation.ValueAnimator
+import android.graphics.Canvas
+import android.graphics.Shader
+import android.text.Layout
+
+private const val TAG_WGHT = "wght"
+private const val DEFAULT_ANIMATION_DURATION: Long = 1000
+
+/**
+ * This class provides text animation between two styles.
+ *
+ * Currently this class can provide text style animation for text weight and text size. For example
+ * the simple view that draws text with animating text size is like as follows:
+ *
+ * <pre>
+ * <code>
+ * class SimpleTextAnimation : View {
+ * @JvmOverloads constructor(...)
+ *
+ * private val layout: Layout = ... // Text layout, e.g. StaticLayout.
+ *
+ * // TextAnimator tells us when needs to be invalidate.
+ * private val animator = TextAnimator(layout) { invalidate() }
+ *
+ * override fun onDraw(canvas: Canvas) = animator.draw(canvas)
+ *
+ * // Change the text size with animation.
+ * fun setTextSize(sizePx: Float, animate: Boolean) {
+ * animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate)
+ * }
+ * }
+ * </code>
+ * </pre>
+ */
+class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
+ // Following two members are for mutable for testing purposes.
+ internal var textInterpolator: TextInterpolator = TextInterpolator(layout)
+ internal var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
+ duration = DEFAULT_ANIMATION_DURATION
+ addUpdateListener {
+ textInterpolator.progress = it.animatedValue as Float
+ invalidateCallback()
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) = textInterpolator.rebase()
+ override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
+ })
+ }
+
+ fun updateLayout(layout: Layout) {
+ textInterpolator.layout = layout
+ }
+
+ var shader: Shader
+ get() = textInterpolator.basePaint.shader.also {
+ require(it === textInterpolator.targetPaint.shader) {
+ "base and target paint has different shader. Usually shader is not interpolatable."
+ }
+ }
+ set(value) {
+ textInterpolator.basePaint.shader = value
+ textInterpolator.targetPaint.shader = value
+ // Shader doesn't change the text layout, so no need to call onTargetPaintModified or
+ // onBasePaintModified
+ }
+
+ fun draw(c: Canvas) = textInterpolator.draw(c)
+
+ /**
+ * Set text style with animation.
+ *
+ * By passing -1 to weight, the view preserve the current weight.
+ * By passing -1 to textSize, the view preserve the current text size.
+ * Bu passing -1 to duration, the default text animation, 1000ms, is used.
+ * By passing false to animate, the text will be updated without animation.
+ *
+ * @param weight an optional text weight.
+ * @param textSize an optional font size.
+ * @param animate an optional boolean indicating true for showing style transition as animation,
+ * false for immediate style transition. True by default.
+ * @param duration an optional animation duration in milliseconds. This is ignored if animate is
+ * false.
+ * @param interpolator an optional time interpolator. If null is passed, last set interpolator
+ * will be used. This is ignored if animate is false.
+ */
+ fun setTextStyle(
+ weight: Int = -1,
+ textSize: Float = -1f,
+ animate: Boolean = true,
+ duration: Long = -1L,
+ interpolator: TimeInterpolator? = null
+ ) {
+ if (animate) {
+ animator.cancel()
+ textInterpolator.rebase()
+ }
+
+ if (textSize >= 0) {
+ textInterpolator.targetPaint.textSize = textSize
+ }
+ if (weight >= 0) {
+ textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+ }
+ textInterpolator.onTargetPaintModified()
+
+ if (animate) {
+ animator.duration = if (duration == -1L) {
+ DEFAULT_ANIMATION_DURATION
+ } else {
+ duration
+ }
+ interpolator?.let { animator.interpolator = it }
+ animator.start()
+ } else {
+ // No animation is requested, thus set base and target state to the same state.
+ textInterpolator.progress = 1f
+ textInterpolator.rebase()
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
new file mode 100644
index 0000000..51148f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard
+
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.fonts.Font
+import android.graphics.text.PositionedGlyphs
+import android.graphics.text.TextRunShaper
+import android.text.Layout
+import android.util.MathUtils
+import java.lang.Math.max
+
+/**
+ * Provide text style linear interpolation for plain text.
+ */
+class TextInterpolator(layout: Layout) {
+ /**
+ * Returns base paint used for interpolation.
+ *
+ * Once you modified the style parameters, you have to call reshapeText to recalculate base text
+ * layout.
+ *
+ * @return a paint object.
+ */
+ val basePaint = Paint(layout.paint)
+
+ /**
+ * Returns target paint used for interpolation.
+ *
+ * Once you modified the style parameters, you have to call reshapeText to recalculate target
+ * text layout.
+ *
+ * @return a paint object
+ */
+ val targetPaint = Paint(layout.paint)
+
+ /**
+ * A class represents a single font run.
+ *
+ * A font run is a range that will be drawn with the same font.
+ */
+ private data class FontRun(
+ val start: Int, // inclusive
+ val end: Int, // exclusive
+ var baseFont: Font,
+ var targetFont: Font
+ ) {
+ val length: Int get() = end - start
+ }
+
+ /**
+ * A class represents text layout of a single line.
+ */
+ private class Line(
+ val glyphIds: IntArray,
+ val baseX: FloatArray, // same length as glyphIds
+ val baseY: FloatArray, // same length as glyphIds
+ val targetX: FloatArray, // same length as glyphIds
+ val targetY: FloatArray, // same length as glyphIds
+ val fontRuns: List<FontRun>
+ )
+
+ private var lines = listOf<Line>()
+ private val fontInterpolator = FontInterpolator()
+
+ // Recycling object for glyph drawing. Will be extended for the longest font run if needed.
+ private val tmpDrawPaint = Paint()
+ private var tmpPositionArray = FloatArray(20)
+
+ /**
+ * The progress position of the interpolation.
+ *
+ * The 0f means the start state, 1f means the end state.
+ */
+ var progress: Float = 0f
+
+ /**
+ * The layout used for drawing text.
+ *
+ * Only non-styled text is supported. Even if the given layout is created from Spanned, the
+ * span information is not used.
+ *
+ * The paint objects used for interpolation are not changed by this method call.
+ *
+ * Note: disabling ligature is strongly recommended if you give extra letter spacing since they
+ * may be disjointed based on letter spacing value and cannot be interpolated. Animator will
+ * throw runtime exception if they cannot be interpolated.
+ */
+ var layout: Layout = layout
+ get() = field
+ set(value) {
+ field = value
+ shapeText(value)
+ }
+
+ init {
+ // shapeText needs to be called after all members are initialized.
+ shapeText(layout)
+ }
+
+ /**
+ * Recalculate internal text layout for interpolation.
+ *
+ * Whenever you modifies target paint, you have to call this method to recalculate internal text
+ * layout used for interpolation.
+ */
+ fun onTargetPaintModified() {
+ updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
+ }
+
+ /**
+ * Recalculate internal text layout for interpolation.
+ *
+ * Whenever you modifies base paint, you have to call this method to recalculate internal text
+ * layout used for interpolation.
+ */
+ fun onBasePaintModified() {
+ updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
+ }
+
+ /**
+ * Rebase the base state to the middle of the interpolation.
+ *
+ * The text interpolator does not calculate all the text position by text shaper due to
+ * performance reasons. Instead, the text interpolator shape the start and end state and
+ * calculate text position of the middle state by linear interpolation. Due to this trick,
+ * the text positions of the middle state is likely different from the text shaper result.
+ * So, if you want to start animation from the middle state, you will see the glyph jumps due to
+ * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different
+ * from text shape result of weight 550.
+ *
+ * After calling this method, do not call onBasePaintModified() since it reshape the text and
+ * update the base state. As in above notice, the text shaping result at current progress is
+ * different shaped result. By calling onBasePaintModified(), you may see the glyph jump.
+ *
+ * By calling this method, the progress will be reset to 0.
+ *
+ * This API is useful to continue animation from the middle of the state. For example, if you
+ * animate weight from 200 to 400, then if you want to move back to 200 at the half of the
+ * animation, it will look like
+ *
+ * <pre>
+ * <code>
+ * val interp = TextInterpolator(layout)
+ *
+ * // Interpolate between weight 200 to 400.
+ * interp.basePaint.fontVariationSettings = "'wght' 200"
+ * interp.onBasePaintModified()
+ * interp.targetPaint.fontVariationSettings = "'wght' 400"
+ * interp.onTargetPaintModified()
+ *
+ * // animate
+ * val animator = ValueAnimator.ofFloat(1f).apply {
+ * addUpdaterListener {
+ * interp.progress = it.animateValue as Float
+ * }
+ * }.start()
+ *
+ * // Here, assuming you receive some event and want to start new animation from current
+ * // state.
+ * OnSomeEvent {
+ * animator.cancel()
+ *
+ * // start another animation from the current state.
+ * interp.rebase() // Use current state as base state.
+ * interp.targetPaint.fontVariationSettings = "'wght' 200" // set new target
+ * interp.onTargetPaintModified() // reshape target
+ *
+ * // Here the textInterpolator interpolate from 'wght' from 300 to 200 if the current
+ * // progress is 0.5
+ * animator.start()
+ * }
+ * </code>
+ * </pre>
+ *
+ */
+ fun rebase() {
+ if (progress == 0f) {
+ return
+ } else if (progress == 1f) {
+ basePaint.set(targetPaint)
+ } else {
+ lerp(basePaint, targetPaint, progress, tmpDrawPaint)
+ basePaint.set(tmpDrawPaint)
+ }
+
+ lines.forEach { line ->
+ for (i in line.baseX.indices) {
+ line.baseX[i] = MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+ line.baseY[i] = MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+ }
+ line.fontRuns.forEach {
+ it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress)
+ }
+ }
+
+ progress = 0f
+ }
+
+ /**
+ * Draws interpolated text at the given progress.
+ *
+ * @param canvas a canvas.
+ */
+ fun draw(canvas: Canvas) {
+ lerp(basePaint, targetPaint, progress, tmpDrawPaint)
+ lines.forEachIndexed { lineNo, line ->
+ canvas.save()
+ try {
+ // Move to drawing origin.
+ val origin = layout.getDrawOrigin(lineNo)
+ canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
+
+ line.fontRuns.forEach { run ->
+ drawFontRun(canvas, line, run, tmpDrawPaint)
+ }
+ } finally {
+ canvas.restore()
+ }
+ }
+ }
+
+ // Shape text with current paint parameters.
+ private fun shapeText(layout: Layout) {
+ val baseLayout = shapeText(layout, basePaint)
+ val targetLayout = shapeText(layout, targetPaint)
+
+ require(baseLayout.size == targetLayout.size) {
+ "The new layout result has different line count."
+ }
+
+ var maxRunLength = 0
+ lines = baseLayout.zip(targetLayout) { base, target ->
+ require(base.glyphCount() == target.glyphCount()) {
+ "Inconsistent glyph count at line ${lines.size}"
+ }
+
+ val glyphCount = base.glyphCount()
+
+ // Good to recycle the array if the existing array can hold the new layout result.
+ val glyphIds = IntArray(glyphCount) {
+ base.getGlyphId(it).also { baseGlyphId ->
+ require(baseGlyphId == target.getGlyphId(it)) {
+ "Inconsistent glyph ID at $it in line ${lines.size}"
+ }
+ }
+ }
+
+ val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
+ val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
+ val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
+ val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+
+ // Calculate font runs
+ val fontRun = mutableListOf<FontRun>()
+ if (glyphCount != 0) {
+ var start = 0
+ var baseFont = base.getFont(start)
+ var targetFont = target.getFont(start)
+ require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+ "Cannot interpolate font at $start ($baseFont vs $targetFont)"
+ }
+
+ for (i in 1 until glyphCount) {
+ val nextBaseFont = base.getFont(i)
+ val nextTargetFont = target.getFont(i)
+
+ if (baseFont !== nextBaseFont) {
+ require(targetFont !== nextTargetFont) {
+ "Base font has changed at $i but target font has not changed."
+ }
+ // Font transition point. push run and reset context.
+ fontRun.add(FontRun(start, i, baseFont, targetFont))
+ maxRunLength = max(maxRunLength, i - start)
+ baseFont = nextBaseFont
+ targetFont = nextTargetFont
+ start = i
+ require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+ "Cannot interpolate font at $start ($baseFont vs $targetFont)"
+ }
+ } else { // baseFont === nextBaseFont
+ require(targetFont === nextTargetFont) {
+ "Base font has not changed at $i but target font has changed."
+ }
+ }
+ }
+ fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
+ maxRunLength = max(maxRunLength, glyphCount - start)
+ }
+ Line(glyphIds, baseX, baseY, targetX, targetY, fontRun)
+ }
+
+ // Update float array used for drawing.
+ if (tmpPositionArray.size < maxRunLength * 2) {
+ tmpPositionArray = FloatArray(maxRunLength * 2)
+ }
+ }
+
+ // Draws single font run.
+ private fun drawFontRun(c: Canvas, line: Line, run: FontRun, paint: Paint) {
+ var arrayIndex = 0
+ for (i in run.start until run.end) {
+ tmpPositionArray[arrayIndex++] =
+ MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+ tmpPositionArray[arrayIndex++] =
+ MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+ }
+
+ c.drawGlyphs(
+ line.glyphIds,
+ run.start,
+ tmpPositionArray,
+ 0,
+ run.length,
+ fontInterpolator.lerp(run.baseFont, run.targetFont, progress),
+ paint)
+ }
+
+ private fun updatePositionsAndFonts(
+ layoutResult: List<PositionedGlyphs>,
+ updateBase: Boolean
+ ) {
+ // Update target positions with newly calculated text layout.
+ check(layoutResult.size == lines.size) {
+ "The new layout result has different line count."
+ }
+
+ lines.zip(layoutResult) { line, newGlyphs ->
+ require(newGlyphs.glyphCount() == line.glyphIds.size) {
+ "The new layout has different glyph count."
+ }
+
+ line.fontRuns.forEach { run ->
+ val newFont = newGlyphs.getFont(run.start)
+ for (i in run.start until run.end) {
+ require(newGlyphs.getGlyphId(run.start) == line.glyphIds[run.start]) {
+ "The new layout has different glyph ID at ${run.start}"
+ }
+ require(newFont === newGlyphs.getFont(i)) {
+ "The new layout has different font run." +
+ " $newFont vs ${newGlyphs.getFont(i)} at $i"
+ }
+ }
+
+ // The passing base font and target font is already interpolatable, so just check
+ // new font can be interpolatable with base font.
+ require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
+ "New font cannot be interpolated with existing font. $newFont, ${run.baseFont}"
+ }
+
+ if (updateBase) {
+ run.baseFont = newFont
+ } else {
+ run.targetFont = newFont
+ }
+ }
+
+ if (updateBase) {
+ for (i in line.baseX.indices) {
+ line.baseX[i] = newGlyphs.getGlyphX(i)
+ line.baseY[i] = newGlyphs.getGlyphY(i)
+ }
+ } else {
+ for (i in line.baseX.indices) {
+ line.targetX[i] = newGlyphs.getGlyphX(i)
+ line.targetY[i] = newGlyphs.getGlyphY(i)
+ }
+ }
+ }
+ }
+
+ // Linear interpolate the paint.
+ private fun lerp(from: Paint, to: Paint, t: Float, out: Paint) {
+ // Currently only font size is interpolated.
+ // TODO(172943390): Add other interpolation or support custom interpolator.
+ out.set(from)
+ out.textSize = MathUtils.lerp(from.textSize, to.textSize, t)
+ }
+
+ // Shape the text and stores the result to out argument.
+ private fun shapeText(layout: Layout, paint: Paint): List<PositionedGlyphs> {
+ val out = mutableListOf<PositionedGlyphs>()
+ for (lineNo in 0 until layout.lineCount) { // Shape all lines.
+ val lineStart = layout.getLineStart(lineNo)
+ val count = layout.getLineEnd(lineNo) - lineStart
+ out.add(TextRunShaper.shapeTextRun(
+ layout.text, // Styles are ignored.
+ lineStart, count, // shape range
+ lineStart, count, // shape context = shape range.
+ 0f, 0f, // the layout offset. Not changed.
+ layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT,
+ paint)) // Use given paint instead of layout's paint for style interpolation.
+ }
+ return out
+ }
+}
+
+private fun Layout.getDrawOrigin(lineNo: Int) =
+ if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
+ getLineLeft(lineNo)
+ } else {
+ getLineRight(lineNo)
+ }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
index 3cbae0a..933d338 100644
--- a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
@@ -71,12 +71,10 @@
public void setDarkAmount(float darkAmount) {
mDarkAmount = darkAmount;
- // TODO: (b/170228350) currently this relayouts throughout the animation;
- // eventually this should use new Text APIs to animate the variable font weight
refreshTime(System.currentTimeMillis());
int weight = (int) MathUtils.lerp(200, 400, 1f - darkAmount);
- mView.setFontVariationSettings("'wght' " + weight);
+ mView.setTextStyle(weight, -1 /* unchange text size */, true);
}
private int getTimeIndex(long timeInMillis) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 7e48edf..11180d1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -110,7 +110,8 @@
.setOneHanded(mWMComponent.getOneHanded())
.setBubbles(mWMComponent.getBubbles())
.setHideDisplayCutout(mWMComponent.getHideDisplayCutout())
- .setShellDump(mWMComponent.getShellDump());
+ .setShellDump(mWMComponent.getShellDump())
+ .setAppPairs(mWMComponent.getAppPairs());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components.
builder = builder.setPip(Optional.ofNullable(null))
@@ -118,7 +119,8 @@
.setOneHanded(Optional.ofNullable(null))
.setBubbles(Optional.ofNullable(null))
.setHideDisplayCutout(Optional.ofNullable(null))
- .setShellDump(Optional.ofNullable(null));
+ .setShellDump(Optional.ofNullable(null))
+ .setAppPairs(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (initializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 724d197..c71bb5b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -17,20 +17,16 @@
package com.android.systemui.accessibility;
import android.annotation.MainThread;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
-import android.view.accessibility.IWindowMagnificationConnectionCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -174,95 +170,4 @@
mAccessibilityManager.setWindowMagnificationConnection(null);
//TODO: destroy controllers.
}
-
- private static class WindowMagnificationConnectionImpl extends
- IWindowMagnificationConnection.Stub {
-
- private static final String TAG = "WindowMagnificationConnectionImpl";
-
- private IWindowMagnificationConnectionCallback mConnectionCallback;
- private final WindowMagnification mWindowMagnification;
- private final Handler mHandler;
- private final ModeSwitchesController mModeSwitchesController;
-
- WindowMagnificationConnectionImpl(@NonNull WindowMagnification windowMagnification,
- @Main Handler mainHandler, ModeSwitchesController modeSwitchesController) {
- mWindowMagnification = windowMagnification;
- mHandler = mainHandler;
- mModeSwitchesController = modeSwitchesController;
- }
-
- @Override
- public void enableWindowMagnification(int displayId, float scale, float centerX,
- float centerY, IRemoteMagnificationAnimationCallback callback) {
- mHandler.post(
- () -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
- centerY, callback));
- }
-
- @Override
- public void setScale(int displayId, float scale) {
- mHandler.post(() -> mWindowMagnification.setScale(displayId, scale));
- }
-
- @Override
- public void disableWindowMagnification(int displayId,
- IRemoteMagnificationAnimationCallback callback) {
- mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
- callback));
- }
-
- @Override
- public void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
- mHandler.post(
- () -> mWindowMagnification.moveWindowMagnifier(displayId, offsetX, offsetY));
- }
-
- @Override
- public void showMagnificationButton(int displayId, int magnificationMode) {
- mHandler.post(
- () -> mModeSwitchesController.showButton(displayId, magnificationMode));
- }
-
- @Override
- public void removeMagnificationButton(int displayId) {
- mHandler.post(
- () -> mModeSwitchesController.removeButton(displayId));
- }
-
- @Override
- public void setConnectionCallback(IWindowMagnificationConnectionCallback callback) {
- mConnectionCallback = callback;
- }
-
- void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
- if (mConnectionCallback != null) {
- try {
- mConnectionCallback.onWindowMagnifierBoundsChanged(displayId, frame);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to inform bounds changed", e);
- }
- }
- }
-
- void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- if (mConnectionCallback != null) {
- try {
- mConnectionCallback.onSourceBoundsChanged(displayId, sourceBounds);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to inform source bounds changed", e);
- }
- }
- }
-
- void onPerformScaleAction(int displayId, float scale) {
- if (mConnectionCallback != null) {
- try {
- mConnectionCallback.onPerformScaleAction(displayId, scale);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to inform performing scale action", e);
- }
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
new file mode 100644
index 0000000..be7d757
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import com.android.systemui.dagger.qualifiers.Main;
+
+/**
+ * Implementation of window magnification connection.
+ *
+ * @see IWindowMagnificationConnection
+ */
+class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
+
+ private static final String TAG = "WindowMagnificationConnectionImpl";
+
+ private IWindowMagnificationConnectionCallback mConnectionCallback;
+ private final WindowMagnification mWindowMagnification;
+ private final Handler mHandler;
+ private final ModeSwitchesController mModeSwitchesController;
+
+ WindowMagnificationConnectionImpl(@NonNull WindowMagnification windowMagnification,
+ @Main Handler mainHandler, ModeSwitchesController modeSwitchesController) {
+ mWindowMagnification = windowMagnification;
+ mHandler = mainHandler;
+ mModeSwitchesController = modeSwitchesController;
+ }
+
+ @Override
+ public void enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY, IRemoteMagnificationAnimationCallback callback) {
+ mHandler.post(
+ () -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
+ centerY, callback));
+ }
+
+ @Override
+ public void setScale(int displayId, float scale) {
+ mHandler.post(() -> mWindowMagnification.setScale(displayId, scale));
+ }
+
+ @Override
+ public void disableWindowMagnification(int displayId,
+ IRemoteMagnificationAnimationCallback callback) {
+ mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
+ callback));
+ }
+
+ @Override
+ public void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ mHandler.post(
+ () -> mWindowMagnification.moveWindowMagnifier(displayId, offsetX, offsetY));
+ }
+
+ @Override
+ public void showMagnificationButton(int displayId, int magnificationMode) {
+ mHandler.post(
+ () -> mModeSwitchesController.showButton(displayId, magnificationMode));
+ }
+
+ @Override
+ public void removeMagnificationButton(int displayId) {
+ mHandler.post(
+ () -> mModeSwitchesController.removeButton(displayId));
+ }
+
+ @Override
+ public void setConnectionCallback(IWindowMagnificationConnectionCallback callback) {
+ mConnectionCallback = callback;
+ }
+
+ void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
+ if (mConnectionCallback != null) {
+ try {
+ mConnectionCallback.onWindowMagnifierBoundsChanged(displayId, frame);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to inform bounds changed", e);
+ }
+ }
+ }
+
+ void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+ if (mConnectionCallback != null) {
+ try {
+ mConnectionCallback.onSourceBoundsChanged(displayId, sourceBounds);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to inform source bounds changed", e);
+ }
+ }
+ }
+
+ void onPerformScaleAction(int displayId, float scale) {
+ if (mConnectionCallback != null) {
+ try {
+ mConnectionCallback.onPerformScaleAction(displayId, scale);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to inform performing scale action", e);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index 9a40541..e4f6d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -196,7 +196,7 @@
public void onAuthenticationFailed(String failureReason) {
if (getSize() == AuthDialog.SIZE_MEDIUM) {
mTryAgainButton.setVisibility(View.VISIBLE);
- mPositiveButton.setVisibility(View.GONE);
+ mConfirmButton.setVisibility(View.GONE);
}
// Do this last since wa want to know if the button is being animated (in the case of
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 0608ca2..c748ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -116,8 +116,16 @@
return mBiometricView.findViewById(R.id.button_negative);
}
- public Button getPositiveButton() {
- return mBiometricView.findViewById(R.id.button_positive);
+ public Button getCancelButton() {
+ return mBiometricView.findViewById(R.id.button_cancel);
+ }
+
+ public Button getUseCredentialButton() {
+ return mBiometricView.findViewById(R.id.button_use_credential);
+ }
+
+ public Button getConfirmButton() {
+ return mBiometricView.findViewById(R.id.button_confirm);
}
public Button getTryAgainButton() {
@@ -176,8 +184,16 @@
private View mIconHolderView;
protected ImageView mIconView;
@VisibleForTesting protected TextView mIndicatorView;
+
+ // Negative button position, exclusively for the app-specified behavior
@VisibleForTesting Button mNegativeButton;
- @VisibleForTesting Button mPositiveButton;
+ // Negative button position, exclusively for cancelling auth after passive auth success
+ @VisibleForTesting Button mCancelButton;
+ // Negative button position, shown if device credentials are allowed
+ @VisibleForTesting Button mUseCredentialButton;
+
+ // Positive button position,
+ @VisibleForTesting Button mConfirmButton;
@VisibleForTesting Button mTryAgainButton;
// Measurements when biometric view is showing text, buttons, etc.
@@ -303,6 +319,7 @@
mDescriptionView.setVisibility(View.GONE);
mIndicatorView.setVisibility(View.GONE);
mNegativeButton.setVisibility(View.GONE);
+ mUseCredentialButton.setVisibility(View.GONE);
final float iconPadding = getResources()
.getDimension(R.dimen.biometric_dialog_icon_padding);
@@ -336,6 +353,7 @@
mTitleView.setAlpha(opacity);
mIndicatorView.setAlpha(opacity);
mNegativeButton.setAlpha(opacity);
+ mCancelButton.setAlpha(opacity);
mTryAgainButton.setAlpha(opacity);
if (!TextUtils.isEmpty(mSubtitleView.getText())) {
@@ -355,7 +373,12 @@
super.onAnimationStart(animation);
mTitleView.setVisibility(View.VISIBLE);
mIndicatorView.setVisibility(View.VISIBLE);
- mNegativeButton.setVisibility(View.VISIBLE);
+
+ if (isDeviceCredentialAllowed()) {
+ mUseCredentialButton.setVisibility(View.VISIBLE);
+ } else {
+ mNegativeButton.setVisibility(View.VISIBLE);
+ }
mTryAgainButton.setVisibility(View.VISIBLE);
if (!TextUtils.isEmpty(mSubtitleView.getText())) {
@@ -447,15 +470,17 @@
case STATE_AUTHENTICATING:
removePendingAnimations();
if (mRequireConfirmation) {
- mPositiveButton.setEnabled(false);
- mPositiveButton.setVisibility(View.VISIBLE);
+ mConfirmButton.setEnabled(false);
+ mConfirmButton.setVisibility(View.VISIBLE);
}
break;
case STATE_AUTHENTICATED:
if (mSize != AuthDialog.SIZE_SMALL) {
- mPositiveButton.setVisibility(View.GONE);
+ mConfirmButton.setVisibility(View.GONE);
mNegativeButton.setVisibility(View.GONE);
+ mUseCredentialButton.setVisibility(View.GONE);
+ mCancelButton.setVisibility(View.GONE);
mIndicatorView.setVisibility(View.INVISIBLE);
}
announceForAccessibility(getResources()
@@ -466,10 +491,11 @@
case STATE_PENDING_CONFIRMATION:
removePendingAnimations();
- mNegativeButton.setText(R.string.cancel);
- mNegativeButton.setContentDescription(getResources().getString(R.string.cancel));
- mPositiveButton.setEnabled(true);
- mPositiveButton.setVisibility(View.VISIBLE);
+ mNegativeButton.setVisibility(View.GONE);
+ mCancelButton.setVisibility(View.VISIBLE);
+ mUseCredentialButton.setVisibility(View.GONE);
+ mConfirmButton.setEnabled(true);
+ mConfirmButton.setVisibility(View.VISIBLE);
mIndicatorView.setTextColor(mTextColorHint);
mIndicatorView.setText(R.string.biometric_dialog_tap_confirm);
mIndicatorView.setVisibility(View.VISIBLE);
@@ -595,23 +621,29 @@
mIconView = mInjector.getIconView();
mIconHolderView = mInjector.getIconHolderView();
mIndicatorView = mInjector.getIndicatorView();
+
+ // Negative-side (left) buttons
mNegativeButton = mInjector.getNegativeButton();
- mPositiveButton = mInjector.getPositiveButton();
+ mCancelButton = mInjector.getCancelButton();
+ mUseCredentialButton = mInjector.getUseCredentialButton();
+
+ // Positive-side (right) buttons
+ mConfirmButton = mInjector.getConfirmButton();
mTryAgainButton = mInjector.getTryAgainButton();
mNegativeButton.setOnClickListener((view) -> {
- if (mState == STATE_PENDING_CONFIRMATION) {
- mCallback.onAction(Callback.ACTION_USER_CANCELED);
- } else {
- if (isDeviceCredentialAllowed()) {
- startTransitionToCredentialUI();
- } else {
- mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
- }
- }
+ mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
});
- mPositiveButton.setOnClickListener((view) -> {
+ mCancelButton.setOnClickListener((view) -> {
+ mCallback.onAction(Callback.ACTION_USER_CANCELED);
+ });
+
+ mUseCredentialButton.setOnClickListener((view) -> {
+ startTransitionToCredentialUI();
+ });
+
+ mConfirmButton.setOnClickListener((view) -> {
updateState(STATE_AUTHENTICATED);
});
@@ -645,31 +677,36 @@
void onAttachedToWindowInternal() {
setText(mTitleView, mPromptInfo.getTitle());
- final CharSequence negativeText;
if (isDeviceCredentialAllowed()) {
-
+ final CharSequence credentialButtonText;
final @Utils.CredentialType int credentialType =
Utils.getCredentialType(mContext, mEffectiveUserId);
-
switch (credentialType) {
case Utils.CREDENTIAL_PIN:
- negativeText = getResources().getString(R.string.biometric_dialog_use_pin);
+ credentialButtonText =
+ getResources().getString(R.string.biometric_dialog_use_pin);
break;
case Utils.CREDENTIAL_PATTERN:
- negativeText = getResources().getString(R.string.biometric_dialog_use_pattern);
+ credentialButtonText =
+ getResources().getString(R.string.biometric_dialog_use_pattern);
break;
case Utils.CREDENTIAL_PASSWORD:
- negativeText = getResources().getString(R.string.biometric_dialog_use_password);
+ credentialButtonText =
+ getResources().getString(R.string.biometric_dialog_use_password);
break;
default:
- negativeText = getResources().getString(R.string.biometric_dialog_use_password);
+ credentialButtonText =
+ getResources().getString(R.string.biometric_dialog_use_password);
break;
}
+ mNegativeButton.setVisibility(View.GONE);
+
+ mUseCredentialButton.setText(credentialButtonText);
+ mUseCredentialButton.setVisibility(View.VISIBLE);
} else {
- negativeText = mPromptInfo.getNegativeButtonText();
+ setText(mNegativeButton, mPromptInfo.getNegativeButtonText());
}
- setText(mNegativeButton, negativeText);
setTextOrHide(mSubtitleView, mPromptInfo.getSubtitle());
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 07e1f1b..2b33f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -640,6 +640,7 @@
}
mContainerState = STATE_SHOWING;
if (mBiometricView != null) {
+ mConfig.mCallback.onDialogAnimatedIn();
mBiometricView.onDialogAnimatedIn();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 3f66c08..ecbe5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -201,6 +201,20 @@
}
@Override
+ public void onDialogAnimatedIn() {
+ if (mReceiver == null) {
+ Log.e(TAG, "onDialogAnimatedIn: Receiver is null");
+ return;
+ }
+
+ try {
+ mReceiver.onDialogAnimatedIn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e);
+ }
+ }
+
+ @Override
public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index d3bd4fb..d8d07e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -65,4 +65,9 @@
* @param event
*/
void onSystemEvent(int event);
+
+ /**
+ * Notifies when the dialog has finished animating in.
+ */
+ void onDialogAnimatedIn();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 02c3c2f..68a28ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -25,6 +25,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.ShellDump;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.onehanded.OneHanded;
@@ -61,6 +62,9 @@
Builder setSplitScreen(Optional<SplitScreen> s);
@BindsInstance
+ Builder setAppPairs(Optional<AppPairs> s);
+
+ @BindsInstance
Builder setOneHanded(Optional<OneHanded> o);
@BindsInstance
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b3e62e52..e634529 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -19,6 +19,7 @@
import com.android.systemui.wmshell.WMShellModule;
import com.android.wm.shell.ShellDump;
import com.android.wm.shell.ShellInit;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.onehanded.OneHanded;
@@ -71,6 +72,9 @@
Optional<SplitScreen> getSplitScreen();
@WMSingleton
+ Optional<AppPairs> getAppPairs();
+
+ @WMSingleton
Optional<Bubbles> getBubbles();
@WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 8415368..9b7cf6e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,9 +16,11 @@
package com.android.systemui.people.widget;
+import android.app.NotificationChannel;
import android.content.ComponentName;
import android.content.Context;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@@ -130,6 +132,18 @@
if (DEBUG) Log.d(TAG, "onNotificationsInitialized");
updateWidgets();
}
+
+ @Override
+ public void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ if (DEBUG) Log.d(TAG, "onNotificationChannelModified");
+ if (channel.isConversation()) {
+ updateWidgets();
+ }
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 80bfa79..f4571d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -65,8 +65,6 @@
private View mQSPanelContainer;
private View mBackground;
- private View mBackgroundGradient;
- private View mStatusBarBackground;
private int mSideMargins;
private boolean mQsDisabled;
@@ -86,8 +84,6 @@
mQSCustomizer = findViewById(R.id.qs_customize);
mDragHandle = findViewById(R.id.qs_drag_handle_view);
mBackground = findViewById(R.id.quick_settings_background);
- mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background);
- mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view);
mHeader.getHeaderQsPanel().setMediaVisibilityChangedListener((visible) -> {
if (mHeader.getHeaderQsPanel().isShown()) {
mAnimateBottomOnNextLayout = true;
@@ -117,7 +113,6 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- setBackgroundGradientVisibility(newConfig);
mSizePoint.set(0, 0); // Will be retrieved on next measure pass.
}
@@ -186,7 +181,6 @@
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
- setBackgroundGradientVisibility(getResources().getConfiguration());
mBackground.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
}
@@ -252,16 +246,6 @@
+ mHeader.getHeight();
}
- private void setBackgroundGradientVisibility(Configuration newConfig) {
- if (newConfig.orientation == ORIENTATION_LANDSCAPE) {
- mBackgroundGradient.setVisibility(View.INVISIBLE);
- mStatusBarBackground.setVisibility(View.INVISIBLE);
- } else {
- mBackgroundGradient.setVisibility(mQsDisabled ? View.INVISIBLE : View.VISIBLE);
- mStatusBarBackground.setVisibility(View.VISIBLE);
- }
- }
-
public void setExpansion(float expansion) {
mQsExpansion = expansion;
mDragHandle.setAlpha(1.0f - expansion);
@@ -271,8 +255,7 @@
private void updatePaddingsAndMargins(QSPanelController qsPanelController) {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
- if (view == mStatusBarBackground || view == mBackgroundGradient
- || view == mQSCustomizer) {
+ if (view == mQSCustomizer) {
// Some views are always full width or have dependent padding
continue;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 0fe018e..043f5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -16,6 +16,9 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
+import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
@@ -50,6 +53,7 @@
import com.android.systemui.util.Utils;
import javax.inject.Inject;
+import javax.inject.Named;
public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks,
StatusBarStateController.StateListener {
@@ -82,6 +86,8 @@
private final InjectionInflationController mInjectionInflater;
private final CommandQueue mCommandQueue;
private final QSDetailDisplayer mQsDetailDisplayer;
+ private final MediaHost mQsMediaHost;
+ private final MediaHost mQqsMediaHost;
private final QSFragmentComponent.Factory mQsComponentFactory;
private final QSTileHost mHost;
private boolean mShowCollapsedOnKeyguard;
@@ -104,12 +110,15 @@
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
InjectionInflationController injectionInflater, QSTileHost qsTileHost,
StatusBarStateController statusBarStateController, CommandQueue commandQueue,
- QSDetailDisplayer qsDetailDisplayer,
+ QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
+ @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
QSFragmentComponent.Factory qsComponentFactory) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mInjectionInflater = injectionInflater;
mCommandQueue = commandQueue;
mQsDetailDisplayer = qsDetailDisplayer;
+ mQsMediaHost = qsMediaHost;
+ mQqsMediaHost = qqsMediaHost;
mQsComponentFactory = qsComponentFactory;
commandQueue.observe(getLifecycle(), this);
mHost = qsTileHost;
@@ -261,7 +270,6 @@
}
public void setHost(QSTileHost qsh) {
- mHeader.setQSPanel(mQSPanelController.getView());
mQSDetail.setHost(qsh);
}
@@ -455,11 +463,9 @@
float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
+ mQSPanelScrollView.getScrollRange();
// The expanded media host should never move below the laid out position
- pinToBottom(
- expandedMediaPosition, mQSPanelController.getMediaHost(), true /* expanded */);
+ pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */);
// The expanded media host should never move above the laid out position
- pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(),
- false /* expanded */);
+ pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 08f38a8..8955a7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -550,6 +550,7 @@
}
boolean horizontal = shouldUseHorizontalLayout();
ViewGroup host = mMediaHost.getHostView();
+
ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
ViewGroup currentParent = (ViewGroup) host.getParent();
if (currentParent != newParent) {
@@ -865,10 +866,6 @@
}
}
- public MediaHost getMediaHost() {
- return mMediaHost;
- }
-
/**
* Set the header container of quick settings.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index addaf7e..32c81af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs;
+import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import android.annotation.NonNull;
@@ -40,6 +41,7 @@
import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* Controller for {@link QSPanel}.
@@ -49,6 +51,7 @@
private final QSSecurityFooter mQsSecurityFooter;
private final TunerService mTunerService;
private final QSCustomizerController mQsCustomizerController;
+ private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final BrightnessController mBrightnessController;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
private final BrightnessSlider mBrightnessSlider;
@@ -73,15 +76,17 @@
@Inject
QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService,
QSTileHost qstileHost, QSCustomizerController qsCustomizerController,
+ @Named(QS_PANEL) MediaHost mediaHost,
QSTileRevealController.Factory qsTileRevealControllerFactory,
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
BrightnessController.Factory brightnessControllerFactory,
BrightnessSlider.Factory brightnessSliderFactory) {
- super(view, qstileHost, qsCustomizerController, qsTileRevealControllerFactory,
- metricsLogger, uiEventLogger, dumpManager);
+ super(view, qstileHost, qsCustomizerController, mediaHost, metricsLogger, uiEventLogger,
+ dumpManager);
mQsSecurityFooter = qsSecurityFooter;
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
+ mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessSliderFactory = brightnessSliderFactory;
@@ -93,6 +98,7 @@
@Override
public void onInit() {
+ super.onInit();
mQsCustomizerController.init();
mBrightnessSlider.init();
}
@@ -114,6 +120,12 @@
}
@Override
+ protected QSTileRevealController createTileRevealController() {
+ return mQsTileRevealControllerFactory.create(
+ this, (PagedTileLayout) mView.createRegularTileLayout());
+ }
+
+ @Override
protected void onViewDetached() {
mTunerService.removeTunable(mView);
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
@@ -159,11 +171,6 @@
}
/** */
- public MediaHost getMediaHost() {
- return mView.getMediaHost();
- }
-
- /** */
public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
mBrightnessMirrorController = brightnessMirrorController;
if (mBrightnessMirrorController != null) {
@@ -280,5 +287,10 @@
public void setFooterPageIndicator(PageIndicator pageIndicator) {
mView.setFooterPageIndicator(pageIndicator);
}
+
+ /** */
+ public boolean isExpanded() {
+ return mView.isExpanded();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index a3daf0e..06bf9ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -48,7 +48,6 @@
implements Dumpable{
protected final QSTileHost mHost;
private final QSCustomizerController mQsCustomizerController;
- private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final MediaHost mMediaHost;
private final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
@@ -74,14 +73,12 @@
};
protected QSPanelControllerBase(T view, QSTileHost host,
- QSCustomizerController qsCustomizerController,
- QSTileRevealController.Factory qsTileRevealControllerFactory,
+ QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
super(view);
mHost = host;
mQsCustomizerController = qsCustomizerController;
- mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
- mMediaHost = mView.getMediaHost();
+ mMediaHost = mediaHost;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
mDumpManager = dumpManager;
@@ -89,10 +86,8 @@
@Override
protected void onViewAttached() {
- QSPanel.QSTileLayout regularTileLayout = mView.createRegularTileLayout();
- if (regularTileLayout instanceof PagedTileLayout) {
- mQsTileRevealController = mQsTileRevealControllerFactory.create(
- (PagedTileLayout) regularTileLayout);
+ mQsTileRevealController = createTileRevealController();
+ if (mQsTileRevealController != null) {
mQsTileRevealController.setExpansion(mRevealExpansion);
}
@@ -119,6 +114,10 @@
mDumpManager.unregisterDumpable(mView.getDumpableTag());
}
+ protected QSTileRevealController createTileRevealController() {
+ return null;
+ }
+
/** */
public void setTiles() {
setTiles(mHost.getTiles(), false);
@@ -126,9 +125,11 @@
/** */
public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
- if (!collapsedView) {
+ // TODO(b/168904199): move this logic into QSPanelController.
+ if (!collapsedView && mQsTileRevealController != null) {
mQsTileRevealController.updateRevealedTiles(tiles);
}
+
for (QSPanelControllerBase.TileRecord record : mRecords) {
mView.removeTile(record);
record.tile.removeCallback(record.callback);
@@ -192,7 +193,6 @@
.collect(Collectors.joining(","));
}
-
/** */
public void setExpanded(boolean expanded) {
mView.setExpanded(expanded);
@@ -234,7 +234,6 @@
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/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 270fcbf..4789239 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.qs;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW;
+
import android.app.AlertDialog;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyEventLogger;
@@ -44,9 +46,10 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
@@ -54,6 +57,7 @@
import com.android.systemui.statusbar.policy.SecurityController;
import javax.inject.Inject;
+import javax.inject.Named;
@QSScope
class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
@@ -81,18 +85,19 @@
private int mFooterIconId;
@Inject
- public QSSecurityFooter(QSPanel qsPanel, Context context, UserTracker userTracker) {
- mRootView = LayoutInflater.from(context)
- .inflate(R.layout.quick_settings_footer, qsPanel, false);
+ QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, Context context,
+ UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
+ SecurityController securityController, @Background Looper bgLooper) {
+ mRootView = rootView;
mRootView.setOnClickListener(this);
mFooterText = mRootView.findViewById(R.id.footer_text);
mFooterIcon = mRootView.findViewById(R.id.footer_icon);
mFooterIconId = R.drawable.ic_info_outline;
mContext = context;
- mMainHandler = new Handler(Looper.myLooper());
- mActivityStarter = Dependency.get(ActivityStarter.class);
- mSecurityController = Dependency.get(SecurityController.class);
- mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
+ mMainHandler = mainHandler;
+ mActivityStarter = activityStarter;
+ mSecurityController = securityController;
+ mHandler = new H(bgLooper);
mUserTracker = userTracker;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
index 9414d0e..3f93108 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
@@ -22,7 +22,7 @@
private static final long QS_REVEAL_TILES_DELAY = 500L;
private final Context mContext;
- private final QSPanel mQSPanel;
+ private final QSPanelController mQSPanelController;
private final PagedTileLayout mPagedTileLayout;
private final QSCustomizerController mQsCustomizerController;
private final ArraySet<String> mTilesToReveal = new ArraySet<>();
@@ -32,17 +32,17 @@
@Override
public void run() {
mPagedTileLayout.startTileReveal(mTilesToReveal, () -> {
- if (mQSPanel.isExpanded()) {
+ if (mQSPanelController.isExpanded()) {
addTileSpecsToRevealed(mTilesToReveal);
mTilesToReveal.clear();
}
});
}
};
- QSTileRevealController(Context context, QSPanel qsPanel, PagedTileLayout pagedTileLayout,
- QSCustomizerController qsCustomizerController) {
+ QSTileRevealController(Context context, QSPanelController qsPanelController,
+ PagedTileLayout pagedTileLayout, QSCustomizerController qsCustomizerController) {
mContext = context;
- mQSPanel = qsPanel;
+ mQSPanelController = qsPanelController;
mPagedTileLayout = pagedTileLayout;
mQsCustomizerController = qsCustomizerController;
}
@@ -85,18 +85,17 @@
@QSScope
static class Factory {
private final Context mContext;
- private final QSPanel mQsPanel;
private final QSCustomizerController mQsCustomizerController;
@Inject
- Factory(Context context, QSPanel qsPanel, QSCustomizerController qsCustomizerController) {
+ Factory(Context context, QSCustomizerController qsCustomizerController) {
mContext = context;
- mQsPanel = qsPanel;
mQsCustomizerController = qsCustomizerController;
}
- QSTileRevealController create(PagedTileLayout pagedTileLayout) {
- return new QSTileRevealController(mContext, mQsPanel, pagedTileLayout,
+ QSTileRevealController create(QSPanelController qsPanelController,
+ PagedTileLayout pagedTileLayout) {
+ return new QSTileRevealController(mContext, qsPanelController, pagedTileLayout,
mQsCustomizerController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index ebe80ae..06e8634 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -51,7 +51,6 @@
private boolean mDisabledByPolicy;
private int mMaxTiles;
- protected QSPanel mFullPanel;
@Inject
@@ -118,10 +117,6 @@
return TAG;
}
- public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
- mFullPanel = fullPanel;
- }
-
@Override
protected boolean shouldShowDetail() {
return !mExpanded;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 54d3026..7f50eef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -16,10 +16,13 @@
package com.android.systemui.qs;
+import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
@@ -28,6 +31,7 @@
import java.util.List;
import javax.inject.Inject;
+import javax.inject.Named;
/** Controller for {@link QuickQSPanel}. */
@QSScope
@@ -46,11 +50,11 @@
@Inject
QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
QSCustomizerController qsCustomizerController,
- QSTileRevealController.Factory qsTileRevealControllerFactory,
+ @Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
DumpManager dumpManager) {
- super(view, qsTileHost, qsCustomizerController, qsTileRevealControllerFactory,
- metricsLogger, uiEventLogger, dumpManager);
+ super(view, qsTileHost, qsCustomizerController, mediaHost, metricsLogger, uiEventLogger,
+ dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 5757602..09894e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -137,10 +137,6 @@
updateResources();
- Rect tintArea = new Rect(0, 0, 0, 0);
- // Set light text on the header icons because they will always be on a black background
- applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
-
mClockView = findViewById(R.id.clock);
mSpace = findViewById(R.id.space);
@@ -153,6 +149,19 @@
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
mRingerModeTextView.setSelected(true);
mNextAlarmTextView.setSelected(true);
+
+ int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.colorForeground);
+ float intensity = getColorIntensity(colorForeground);
+ int fillColor = mDualToneHandler.getSingleColor(intensity);
+
+ Rect tintArea = new Rect(0, 0, 0, 0);
+ mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor);
+
+ // The quick settings status bar clock depends on the color of the background scrim and
+ // can be different from the status bar clock color.
+ mClockView.setTextColor(
+ Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
}
void onAttach(TintedIconManager iconManager) {
@@ -225,13 +234,6 @@
!Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
}
- private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
- View v = findViewById(id);
- if (v instanceof DarkReceiver) {
- ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
- }
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -439,18 +441,6 @@
post(() -> setClickable(!mExpanded));
}
- public void setQSPanel(final QSPanel qsPanel) {
- //host.setHeaderView(mExpandIndicator);
- mHeaderQsPanel.setQSPanelAndHeader(qsPanel, this);
-
- Rect tintArea = new Rect(0, 0, 0, 0);
- int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.colorForeground);
- float intensity = getColorIntensity(colorForeground);
- int fillColor = mDualToneHandler.getSingleColor(intensity);
- mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor);
- }
-
public void setCallback(Callback qsPanelCallback) {
mHeaderQsPanel.setCallback(qsPanelCallback);
}
@@ -475,6 +465,7 @@
return mLifecycle;
}
+ /** */
public void setContentMargins(int marginStart, int marginEnd) {
mContentMarginStart = marginStart;
mContentMarginEnd = marginEnd;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 32904a2..5ee9df4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -34,8 +34,10 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.plugins.ActivityStarter;
@@ -99,6 +101,9 @@
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
+
+ private SysuiColorExtractor mColorExtractor;
+ private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
private final ZenModeController.Callback mZenModeControllerCallback = new Callback() {
@@ -216,7 +221,8 @@
CommandQueue commandQueue, DemoModeController demoModeController,
UserTracker userTracker, QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
- PrivacyLogger privacyLogger) {
+ PrivacyLogger privacyLogger,
+ SysuiColorExtractor colorExtractor) {
super(view);
mZenModeController = zenModeController;
mNextAlarmController = nextAlarmController;
@@ -246,6 +252,12 @@
mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, mCommandQueue);
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
+ mColorExtractor = colorExtractor;
+ mOnColorsChangedListener = (extractor, which) -> {
+ final boolean lightTheme = mColorExtractor.getNeutralColors().supportsDarkText();
+ mClockView.onColorsChanged(lightTheme);
+ };
+ mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
}
@Override
@@ -281,6 +293,7 @@
protected void onViewDetached() {
mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner);
mClockView.setOnClickListener(null);
+ mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
mNextAlarmContainer.setOnClickListener(null);
mRingerContainer.setOnClickListener(null);
mPrivacyChip.setOnClickListener(null);
@@ -366,7 +379,6 @@
mZenModeController.getConsolidatedPolicy());
}
-
private static class ClockDemoModeReceiver implements DemoMode {
private Clock mClockView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 354b2c9..f3bf306 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.dagger;
+import android.view.LayoutInflater;
import android.view.View;
import com.android.systemui.R;
@@ -31,6 +32,8 @@
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
+import javax.inject.Named;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -40,6 +43,8 @@
*/
@Module
public interface QSFragmentModule {
+ String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
+
/** */
@Provides
@RootView
@@ -95,4 +100,12 @@
static QSCustomizer providesQSCutomizer(@RootView View view) {
return view.findViewById(R.id.qs_customize);
}
-}
+
+ /** */
+ @Provides
+ @QSScope
+ @Named(QS_SECURITY_FOOTER_VIEW)
+ static View providesQSSecurityFooterView(LayoutInflater layoutInflater, QSPanel qsPanel) {
+ return layoutInflater.inflate(R.layout.quick_settings_footer, qsPanel, false);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index f9d2d31..7f31fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.SuppressLint;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
@@ -159,6 +160,19 @@
}
@Override
+ public void onNotificationChannelModified(
+ String pkgName, UserHandle user, NotificationChannel channel, int modificationType) {
+ if (DEBUG) Log.d(TAG, "onNotificationChannelModified");
+ if (!onPluginNotificationChannelModified(pkgName, user, channel, modificationType)) {
+ mMainHandler.post(() -> {
+ for (NotificationHandler handler : mNotificationHandlers) {
+ handler.onNotificationChannelModified(pkgName, user, channel, modificationType);
+ }
+ });
+ }
+ }
+
+ @Override
public void onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons) {
for (NotificationSettingsListener listener : mSettingsListeners) {
listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons);
@@ -229,6 +243,14 @@
void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason);
void onNotificationRankingUpdate(RankingMap rankingMap);
+ /** Called after a notification channel is modified. */
+ default void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ }
+
/**
* Called after the listener has connected to NoMan and posted any current notifications.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 1710daa..09ae7eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -19,6 +19,8 @@
import static java.util.Objects.requireNonNull;
import android.annotation.MainThread;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -158,6 +160,15 @@
public void onNotificationsInitialized() {
mHandler.onNotificationsInitialized();
}
+
+ @Override
+ public void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ mHandler.onNotificationChannelModified(pkgName, user, channel, modificationType);
+ }
};
private void maybeEmitBatch(StatusBarNotification sbn) {
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 2253b2b..3e23a93 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
@@ -1163,9 +1163,7 @@
*/
@Nullable
public NotificationMenuRowPlugin createMenu() {
- final boolean removeShelf = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0 /* show shelf by default */) == 1;
- if (mMenuRow == null || removeShelf) {
+ if (mMenuRow == null) {
return null;
}
if (mMenuRow.getMenuView() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 65a72cc..447fa43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -294,6 +294,11 @@
} else {
mMenuContainer = new FrameLayout(mContext);
}
+ final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0) == 1;
+ if (newFlowHideShelf) {
+ return;
+ }
List<MenuItem> menuItems = mOnLeft ? mLeftMenuItems : mRightMenuItems;
for (int i = 0; i < menuItems.size(); i++) {
addMenuView(menuItems.get(i), mMenuContainer);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
index 9e561d1..4651e8446 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
@@ -14,9 +14,11 @@
package com.android.systemui.statusbar.phone;
+import android.app.NotificationChannel;
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -78,7 +80,7 @@
/**
* Called when listener receives a onNotificationPosted.
- * Returns true to indicate this callback should be skipped.
+ * Returns true if there's a plugin determining to skip the default callbacks.
*/
public boolean onPluginNotificationPosted(StatusBarNotification sbn,
final RankingMap rankingMap) {
@@ -92,7 +94,7 @@
/**
* Called when listener receives a onNotificationRemoved.
- * Returns true to indicate this callback should be skipped.
+ * Returns true if there's a plugin determining to skip the default callbacks.
*/
public boolean onPluginNotificationRemoved(StatusBarNotification sbn,
final RankingMap rankingMap) {
@@ -104,6 +106,20 @@
return false;
}
+ /**
+ * Called when listener receives a onNotificationChannelModified.
+ * Returns true if there's a plugin determining to skip the default callbacks.
+ */
+ public boolean onPluginNotificationChannelModified(
+ String pkgName, UserHandle user, NotificationChannel channel, int modificationType) {
+ for (NotificationListenerController plugin : mPlugins) {
+ if (plugin.onNotificationChannelModified(pkgName, user, channel, modificationType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public RankingMap onPluginRankingUpdate(RankingMap rankingMap) {
return getCurrentRanking();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 7c41bcd..c0a5ffa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -66,6 +66,7 @@
}
};
private DarkReceiver mBattery;
+ private DarkReceiver mClock;
private int mRotationOrientation = -1;
@Nullable
private View mCenterIconSpace;
@@ -99,6 +100,7 @@
@Override
public void onFinishInflate() {
mBattery = findViewById(R.id.battery);
+ mClock = findViewById(R.id.clock);
mCutoutSpace = findViewById(R.id.cutout_space_view);
mCenterIconSpace = findViewById(R.id.centered_icon_area);
@@ -110,6 +112,7 @@
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
+ Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClock);
if (updateOrientationAndCutout()) {
updateLayoutForCutout();
}
@@ -119,6 +122,7 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
+ Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mClock);
mDisplayCutout = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index ef35a3c..fbbf362 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.ColorInt;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -35,6 +36,7 @@
import android.text.style.CharacterStyle;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.View;
import android.widget.TextView;
@@ -96,7 +98,6 @@
private static final int AM_PM_STYLE_GONE = 2;
private final int mAmPmStyle;
- private final boolean mShowDark;
private boolean mShowSeconds;
private Handler mSecondsHandler;
@@ -126,7 +127,6 @@
0, 0);
try {
mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
- mShowDark = a.getBoolean(R.styleable.Clock_showDark, true);
mNonAdaptedColor = getCurrentTextColor();
} finally {
a.recycle();
@@ -196,9 +196,6 @@
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
StatusBarIconController.ICON_HIDE_LIST);
mCommandQueue.addCallback(this);
- if (mShowDark) {
- Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
- }
mCurrentUserTracker.startTracking();
mCurrentUserId = mCurrentUserTracker.getCurrentUserId();
}
@@ -229,9 +226,6 @@
mAttached = false;
Dependency.get(TunerService.class).removeTunable(this);
mCommandQueue.removeCallback(this);
- if (mShowDark) {
- Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
- }
mCurrentUserTracker.stopTracking();
}
}
@@ -334,6 +328,13 @@
}
}
+ // Update text color based when shade scrim changes color.
+ public void onColorsChanged(boolean lightTheme) {
+ final Context context = new ContextThemeWrapper(mContext,
+ lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
+ setTextColor(Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor));
+ }
+
@Override
public void onDensityOrFontScaleChanged() {
FontSizeUtils.updateFontSize(this, R.dimen.status_bar_clock_size);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 4552026..79f0915 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -45,7 +45,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentListener;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
@@ -74,6 +74,7 @@
import com.android.systemui.statusbar.phone.LightBarController;
import java.util.HashMap;
+import java.util.Map;
import java.util.function.Consumer;
/**
@@ -585,27 +586,32 @@
protected void onFinishInflate() {
super.onFinishInflate();
if (mSupportedMimeTypes != null && mSupportedMimeTypes.length > 0) {
- setOnReceiveContentCallback(mSupportedMimeTypes,
- new OnReceiveContentCallback<View>() {
+ setOnReceiveContentListener(mSupportedMimeTypes,
+ new OnReceiveContentListener() {
@Override
- public boolean onReceiveContent(@NonNull View view,
+ @Nullable
+ public Payload onReceiveContent(@NonNull View view,
@NonNull Payload payload) {
- ClipData clip = payload.getClip();
- if (clip.getItemCount() == 0) {
- return false;
- }
- Uri contentUri = clip.getItemAt(0).getUri();
- ClipDescription description = clip.getDescription();
- String mimeType = null;
- if (description.getMimeTypeCount() > 0) {
- mimeType = description.getMimeType(0);
- }
- if (mimeType != null) {
+ Map<Boolean, Payload> split = payload.partition(
+ item -> item.getUri() != null);
+ Payload uriItems = split.get(true);
+ Payload remainingItems = split.get(false);
+ if (uriItems != null) {
+ ClipData clip = uriItems.getClip();
+ ClipDescription description = clip.getDescription();
+ if (clip.getItemCount() > 1
+ || description.getMimeTypeCount() < 1
+ || remainingItems != null) {
+ // TODO(b/172363500): Update to loop over all the items
+ return payload;
+ }
+ Uri contentUri = clip.getItemAt(0).getUri();
+ String mimeType = description.getMimeType(0);
Intent dataIntent = mRemoteInputView
.prepareRemoteInputFromData(mimeType, contentUri);
mRemoteInputView.sendRemoteInput(dataIntent);
}
- return true;
+ return remainingItems;
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 04f1f86..c36acf5 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -53,6 +53,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellDump;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
@@ -98,6 +99,7 @@
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final ProtoTracer mProtoTracer;
private final Optional<ShellDump> mShellDump;
+ private final Optional<AppPairs> mAppPairsOptional;
private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
@@ -116,7 +118,8 @@
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
ProtoTracer protoTracer,
- Optional<ShellDump> shellDump) {
+ Optional<ShellDump> shellDump,
+ Optional<AppPairs> appPairsOptional) {
super(context);
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
@@ -131,6 +134,7 @@
mProtoTracer = protoTracer;
mProtoTracer.add(this);
mShellDump = shellDump;
+ mAppPairsOptional = appPairsOptional;
}
@Override
@@ -335,6 +339,21 @@
}
return true;
}
+
+ case "pair": {
+ String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+ final int taskId1 = new Integer(groups[0]);
+ final int taskId2 = new Integer(groups[1]);
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
+ return true;
+ }
+
+ case "unpair": {
+ String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+ final int taskId = new Integer(groups[0]);
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
+ return true;
+ }
}
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 8c2980f..74aa1a7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -32,6 +32,7 @@
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.AnimationThread;
@@ -75,11 +76,13 @@
static ShellInit provideShellInit(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<SplitScreen> splitScreenOptional) {
+ Optional<SplitScreen> splitScreenOptional,
+ Optional<AppPairs> appPairsOptional) {
return new ShellInit(displayImeController,
dragAndDropController,
shellTaskOrganizer,
- splitScreenOptional);
+ splitScreenOptional,
+ appPairsOptional);
}
/**
@@ -92,9 +95,10 @@
Optional<SplitScreen> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout) {
+ Optional<HideDisplayCutout> hideDisplayCutout,
+ Optional<AppPairs> appPairsOptional) {
return Optional.of(new ShellDump(shellTaskOrganizer, splitScreenOptional, pipOptional,
- oneHandedOptional, hideDisplayCutout));
+ oneHandedOptional, hideDisplayCutout, appPairsOptional));
}
@WMSingleton
@@ -187,6 +191,9 @@
@BindsOptionalOf
abstract SplitScreen optionalSplitScreen();
+ @BindsOptionalOf
+ abstract AppPairs optionalAppPairs();
+
@WMSingleton
@Provides
static Optional<Bubbles> provideBubbles(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index e9c4b0b..ef8a08c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -24,6 +24,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -81,6 +83,13 @@
@WMSingleton
@Provides
+ static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue) {
+ return new AppPairsController(shellTaskOrganizer, syncQueue);
+ }
+
+ @WMSingleton
+ @Provides
static Optional<Pip> providePip(Context context, DisplayController displayController,
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/FontInterpolatorTest.kt
new file mode 100644
index 0000000..95fa3b9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/FontInterpolatorTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard
+
+import android.graphics.Paint
+import android.graphics.fonts.Font
+import android.graphics.fonts.FontVariationAxis
+import android.graphics.text.TextRunShaper
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FontInterpolatorTest : SysuiTestCase() {
+
+ private val sFont = TextRunShaper.shapeTextRun("A", 0, 1, 0, 1, 0f, 0f, false, Paint())
+ .getFont(0)
+
+ private fun assertSameAxes(expect: Font, actual: Font) {
+ val expectAxes = expect.axes?.also { it.sortBy { axis -> axis.tag } }
+ val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } }
+ assertThat(expectAxes).isEqualTo(actualAxes)
+ }
+
+ private fun assertSameAxes(expectVarSettings: String, actual: Font) {
+
+ val expectAxes = FontVariationAxis.fromFontVariationSettings(expectVarSettings)?.also {
+ it.sortBy { axis -> axis.tag }
+ }
+ val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } }
+ assertThat(expectAxes).isEqualTo(actualAxes)
+ }
+
+ @Test
+ fun textInterpolation() {
+ val startFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 100, 'ital' 0, 'GRAD' 200")
+ .build()
+ val endFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 900, 'ital' 1, 'GRAD' 700")
+ .build()
+
+ val interp = FontInterpolator()
+ assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f))
+ assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f))
+ assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
+ }
+
+ @Test
+ fun textInterpolation_DefaultValue() {
+ val startFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 100")
+ .build()
+ val endFont = Font.Builder(sFont)
+ .setFontVariationSettings("'ital' 1")
+ .build()
+
+ val interp = FontInterpolator()
+ assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
+ }
+
+ @Test
+ fun testInterpCache() {
+ val startFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 100")
+ .build()
+ val endFont = Font.Builder(sFont)
+ .setFontVariationSettings("'ital' 1")
+ .build()
+
+ val interp = FontInterpolator()
+ val resultFont = interp.lerp(startFont, endFont, 0.5f)
+ val cachedFont = interp.lerp(startFont, endFont, 0.5f)
+ assertThat(resultFont).isSameInstanceAs(cachedFont)
+ }
+
+ @Test
+ fun testAxesCache() {
+ val startFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 100")
+ .build()
+ val endFont = Font.Builder(sFont)
+ .setFontVariationSettings("'ital' 1")
+ .build()
+
+ val interp = FontInterpolator()
+ val resultFont = interp.lerp(startFont, endFont, 0.5f)
+ val reversedFont = interp.lerp(endFont, startFont, 0.5f)
+ assertThat(resultFont).isSameInstanceAs(reversedFont)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
new file mode 100644
index 0000000..516d015
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard
+
+import android.animation.ValueAnimator
+import android.graphics.Paint
+import android.testing.AndroidTestingRunner
+import android.text.Layout
+import android.text.StaticLayout
+import android.text.TextPaint
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+import kotlin.math.ceil
+
+private val PAINT = TextPaint().apply {
+ textSize = 32f
+}
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class TextAnimatorTest : SysuiTestCase() {
+
+ private fun makeLayout(text: String, paint: TextPaint): Layout {
+ val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt()
+ return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build()
+ }
+
+ @Test
+ fun testAnimationStarted() {
+ val layout = makeLayout("Hello, World", PAINT)
+ val valueAnimator = mock(ValueAnimator::class.java)
+ val textInterpolator = mock(TextInterpolator::class.java)
+ val paint = mock(Paint::class.java)
+ `when`(textInterpolator.targetPaint).thenReturn(paint)
+
+ val textAnimator = TextAnimator(layout, {}).apply {
+ this.textInterpolator = textInterpolator
+ this.animator = valueAnimator
+ }
+
+ textAnimator.setTextStyle(
+ weight = 400,
+ animate = true
+ )
+
+ // If animation is requested, the base state should be rebased and the target state should
+ // be updated.
+ val order = inOrder(textInterpolator)
+ order.verify(textInterpolator).rebase()
+ order.verify(textInterpolator).onTargetPaintModified()
+
+ // In case of animation, should not shape the base state since the animation should start
+ // from current state.
+ verify(textInterpolator, never()).onBasePaintModified()
+
+ // Then, animation should be started.
+ verify(valueAnimator, times(1)).start()
+ }
+
+ @Test
+ fun testAnimationNotStarted() {
+ val layout = makeLayout("Hello, World", PAINT)
+ val valueAnimator = mock(ValueAnimator::class.java)
+ val textInterpolator = mock(TextInterpolator::class.java)
+ val paint = mock(Paint::class.java)
+ `when`(textInterpolator.targetPaint).thenReturn(paint)
+
+ val textAnimator = TextAnimator(layout, {}).apply {
+ this.textInterpolator = textInterpolator
+ this.animator = valueAnimator
+ }
+
+ textAnimator.setTextStyle(
+ weight = 400,
+ animate = false
+ )
+
+ // If animation is not requested, the progress should be 1 which is end of animation and the
+ // base state is rebased to target state by calling rebase.
+ val order = inOrder(textInterpolator)
+ order.verify(textInterpolator).onTargetPaintModified()
+ order.verify(textInterpolator).progress = 1f
+ order.verify(textInterpolator).rebase()
+
+ // Then, animation start should not be called.
+ verify(valueAnimator, never()).start()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
new file mode 100644
index 0000000..65ffcfc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.testing.AndroidTestingRunner
+import android.text.Layout
+import android.text.StaticLayout
+import android.text.TextPaint
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.math.ceil
+
+private const val TEXT = "Hello, World."
+private const val BMP_WIDTH = 400
+private const val BMP_HEIGHT = 300
+
+private val PAINT = TextPaint().apply {
+ textSize = 32f
+}
+
+private val START_PAINT = TextPaint(PAINT).apply {
+ fontVariationSettings = "'wght' 400"
+}
+
+private val END_PAINT = TextPaint(PAINT).apply {
+ fontVariationSettings = "'wght' 700"
+}
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class TextInterpolatorTest : SysuiTestCase() {
+
+ private fun makeLayout(text: String, paint: TextPaint): Layout {
+ val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt()
+ return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build()
+ }
+
+ @Test
+ fun testStartState() {
+ val layout = makeLayout(TEXT, PAINT)
+
+ val interp = TextInterpolator(layout)
+ interp.basePaint.set(START_PAINT)
+ interp.onBasePaintModified()
+
+ interp.targetPaint.set(END_PAINT)
+ interp.onTargetPaintModified()
+
+ // Just after created TextInterpolator, it should have 0 progress.
+ assertThat(interp.progress).isEqualTo(0f)
+ val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ val expected = makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+ assertThat(expected.sameAs(actual)).isTrue()
+ }
+
+ @Test
+ fun testEndState() {
+ val layout = makeLayout(TEXT, PAINT)
+
+ val interp = TextInterpolator(layout)
+ interp.basePaint.set(START_PAINT)
+ interp.onBasePaintModified()
+
+ interp.targetPaint.set(END_PAINT)
+ interp.onTargetPaintModified()
+
+ interp.progress = 1f
+ val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ val expected = makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+ assertThat(expected.sameAs(actual)).isTrue()
+ }
+
+ @Test
+ fun testMiddleState() {
+ val layout = makeLayout(TEXT, PAINT)
+
+ val interp = TextInterpolator(layout)
+ interp.basePaint.set(START_PAINT)
+ interp.onBasePaintModified()
+
+ interp.targetPaint.set(END_PAINT)
+ interp.onTargetPaintModified()
+
+ // We cannot expect exact text layout of the middle position since we don't use text shaping
+ // result for the middle state for performance reason. Just check it is not equals to start
+ // end state.
+ interp.progress = 0.5f
+ val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)))
+ .isFalse()
+ assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)))
+ .isFalse()
+ }
+
+ @Test
+ fun testRebase() {
+ val layout = makeLayout(TEXT, PAINT)
+
+ val interp = TextInterpolator(layout)
+ interp.basePaint.set(START_PAINT)
+ interp.onBasePaintModified()
+
+ interp.targetPaint.set(END_PAINT)
+ interp.onTargetPaintModified()
+
+ interp.progress = 0.5f
+ val expected = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+ // Rebase base state to the current state of progress 0.5.
+ interp.rebase()
+ assertThat(interp.progress).isEqualTo(0f)
+ val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+ assertThat(expected.sameAs(actual)).isTrue()
+ }
+}
+
+private fun Layout.toBitmap(width: Int, height: Int) =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }!!
+
+private fun TextInterpolator.toBitmap(width: Int, height: Int) =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
index b907cdb..043bd5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
@@ -50,8 +50,12 @@
private TestableFaceView mFaceView;
@Mock private Button mNegativeButton;
- @Mock private Button mPositiveButton;
+ @Mock private Button mCancelButton;
+ @Mock private Button mUseCredentialButton;
+
+ @Mock private Button mConfirmButton;
@Mock private Button mTryAgainButton;
+
@Mock private TextView mErrorView;
@Before
@@ -60,9 +64,14 @@
mFaceView = new TestableFaceView(mContext);
mFaceView.mIconController = mock(TestableFaceView.TestableIconController.class);
mFaceView.setCallback(mCallback);
+
mFaceView.mNegativeButton = mNegativeButton;
- mFaceView.mPositiveButton = mPositiveButton;
+ mFaceView.mCancelButton = mCancelButton;
+ mFaceView.mUseCredentialButton = mUseCredentialButton;
+
+ mFaceView.mConfirmButton = mConfirmButton;
mFaceView.mTryAgainButton = mTryAgainButton;
+
mFaceView.mIndicatorView = mErrorView;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index e2517f2..49282ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -60,8 +60,12 @@
@Mock private AuthPanelController mPanelController;
@Mock private Button mNegativeButton;
+ @Mock private Button mCancelButton;
+ @Mock private Button mUseCredentialButton;
+
@Mock private Button mPositiveButton;
@Mock private Button mTryAgainButton;
+
@Mock private TextView mTitleView;
@Mock private TextView mSubtitleView;
@Mock private TextView mDescriptionView;
@@ -89,15 +93,31 @@
@Test
public void testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ final Button negativeButton = new Button(mContext);
+ final Button cancelButton = new Button(mContext);
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
+ @Override
+ public Button getNegativeButton() {
+ return negativeButton;
+ }
+
+ @Override
+ public Button getCancelButton() {
+ return cancelButton;
+ }
+ });
mBiometricView.setRequireConfirmation(true);
mBiometricView.onAuthenticationSucceeded();
waitForIdleSync();
assertEquals(AuthBiometricView.STATE_PENDING_CONFIRMATION, mBiometricView.mState);
verify(mCallback, never()).onAction(anyInt());
- verify(mBiometricView.mNegativeButton).setText(eq(R.string.cancel));
- verify(mBiometricView.mPositiveButton).setEnabled(eq(true));
+
+ assertEquals(View.GONE, negativeButton.getVisibility());
+ assertEquals(View.VISIBLE, cancelButton.getVisibility());
+ assertTrue(cancelButton.isEnabled());
+
+ verify(mBiometricView.mConfirmButton).setEnabled(eq(true));
verify(mIndicatorView).setText(eq(R.string.biometric_dialog_tap_confirm));
verify(mIndicatorView).setVisibility(eq(View.VISIBLE));
}
@@ -107,7 +127,7 @@
Button button = new Button(mContext);
initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
- public Button getPositiveButton() {
+ public Button getConfirmButton() {
return button;
}
});
@@ -137,18 +157,26 @@
}
@Test
- public void testNegativeButton_whenPendingConfirmation_sendsActionUserCanceled() {
- Button button = new Button(mContext);
+ public void testCancelButton_whenPendingConfirmation_sendsActionUserCanceled() {
+ Button cancelButton = new Button(mContext);
+ Button negativeButton = new Button(mContext);
initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getNegativeButton() {
- return button;
+ return negativeButton;
+ }
+ @Override
+ public Button getCancelButton() {
+ return cancelButton;
}
});
mBiometricView.setRequireConfirmation(true);
mBiometricView.onAuthenticationSucceeded();
- button.performClick();
+
+ assertEquals(View.GONE, negativeButton.getVisibility());
+
+ cancelButton.performClick();
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED);
@@ -282,16 +310,23 @@
}
@Test
- public void testNegativeButton_whenDeviceCredentialAllowed() {
- Button negativeButton = new Button(mContext);
+ public void testCredentialButton_whenDeviceCredentialAllowed() {
+ final Button negativeButton = new Button(mContext);
+ final Button useCredentialButton = new Button(mContext);
initDialog(mContext, true /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getNegativeButton() {
return negativeButton;
}
+
+ @Override
+ public Button getUseCredentialButton() {
+ return useCredentialButton;
+ }
});
- negativeButton.performClick();
+ assertEquals(View.GONE, negativeButton.getVisibility());
+ useCredentialButton.performClick();
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
@@ -361,7 +396,17 @@
}
@Override
- public Button getPositiveButton() {
+ public Button getCancelButton() {
+ return mCancelButton;
+ }
+
+ @Override
+ public Button getUseCredentialButton() {
+ return mUseCredentialButton;
+ }
+
+ @Override
+ public Button getConfirmButton() {
return mPositiveButton;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 8ceebeb..3e44fa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.people.widget;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -26,8 +28,10 @@
import static java.util.Objects.requireNonNull;
+import android.app.NotificationChannel;
import android.content.Context;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -56,6 +60,10 @@
private static final String TEST_PACKAGE_A = "com.test.package_a";
private static final String TEST_PACKAGE_B = "com.test.package_b";
+ private static final String TEST_CHANNEL_ID = "channel_id";
+ private static final String TEST_CHANNEL_NAME = "channel_name";
+ private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
+ private static final String TEST_CONVERSATION_ID = "conversation_id";
private PeopleSpaceWidgetManager mManager;
@@ -100,7 +108,7 @@
}
@Test
- public void testNotifyAppWidgetIfWidgets() throws RemoteException {
+ public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -117,7 +125,7 @@
}
@Test
- public void testNotifyAppWidgetTwiceIfTwoNotifications() throws RemoteException {
+ public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
int[] widgetIdsArray = {1, 2};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -149,4 +157,40 @@
verify(mIAppWidgetService, times(2))
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
}
+
+ @Test
+ public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+ int[] widgetIdsArray = {1};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotificationChannel channel =
+ mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME);
+
+ mNoMan.issueChannelModification(TEST_PACKAGE_A,
+ UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mIAppWidgetService, never()).getAppWidgetIds(any());
+ verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+
+ }
+
+ @Test
+ public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+ int[] widgetIdsArray = {1};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotificationChannel channel =
+ mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME);
+ channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID);
+
+ mNoMan.issueChannelModification(TEST_PACKAGE_A,
+ UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mIAppWidgetService, times(1)).getAppWidgetIds(any());
+ verify(mIAppWidgetService, times(1))
+ .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 858ecdc..a6b0330 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.qs.logging.QSLogger;
@@ -82,6 +83,10 @@
private QSFragmentComponent mQsFragmentComponent;
@Mock
private QSPanelController mQSPanelController;
+ @Mock
+ private MediaHost mQSMediaHost;
+ @Mock
+ private MediaHost mQQSMediaHost;
public QSFragmentTest() {
super(QSFragment.class);
@@ -168,6 +173,8 @@
mock(StatusBarStateController.class),
commandQueue,
new QSDetailDisplayer(),
+ mQSMediaHost,
+ mQQSMediaHost,
mQsComponentFactory);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 9421cd0..0fe44ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -86,13 +86,17 @@
private QSPanelControllerBase<QSPanel> mController;
/** Implementation needed to ensure we have a reflectively-available class name. */
- private static class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
+ private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
- QSCustomizerController qsCustomizerController,
- QSTileRevealController.Factory qsTileRevealControllerFactory,
+ QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
- super(view, host, qsCustomizerController, qsTileRevealControllerFactory, metricsLogger,
- uiEventLogger, dumpManager);
+ super(view, host, qsCustomizerController, mediaHost,
+ metricsLogger, uiEventLogger, dumpManager);
+ }
+
+ @Override
+ protected QSTileRevealController createTileRevealController() {
+ return mQSTileRevealController;
}
}
@@ -100,7 +104,6 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mQSPanel.getMediaHost()).thenReturn(mMediaHost);
when(mQSPanel.isAttachedToWindow()).thenReturn(true);
when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED);
@@ -108,11 +111,11 @@
when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout);
when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
- when(mQSTileRevealControllerFactory.create(any())).thenReturn(mQSTileRevealController);
+ when(mQSTileRevealControllerFactory.create(any(), any()))
+ .thenReturn(mQSTileRevealController);
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
- mQSCustomizerController, mQSTileRevealControllerFactory, mMetricsLogger,
- mUiEventLogger, mDumpManager);
+ mQSCustomizerController, mMediaHost, mMetricsLogger, mUiEventLogger, mDumpManager);
mController.init();
reset(mQSTileRevealController);
@@ -122,9 +125,14 @@
public void testSetRevealExpansion_preAttach() {
mController.onViewDetached();
- QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
- mQSTileHost, mQSCustomizerController, mQSTileRevealControllerFactory,
- mMetricsLogger, mUiEventLogger, mDumpManager);
+ QSPanelControllerBase<QSPanel> controller = new QSPanelControllerBase<QSPanel>(
+ mQSPanel, mQSTileHost, mQSCustomizerController, mMediaHost, mMetricsLogger,
+ mUiEventLogger, mDumpManager) {
+ @Override
+ protected QSTileRevealController createTileRevealController() {
+ return mQSTileRevealController;
+ }
+ };
// Nothing happens until attached
controller.setRevealExpansion(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index f57e3c2..9090b9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -41,6 +41,7 @@
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.settings.brightness.ToggleSlider;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.animation.DisappearParameters;
import org.junit.Before;
import org.junit.Test;
@@ -97,7 +98,6 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mQSPanel.getMediaHost()).thenReturn(mMediaHost);
when(mQSPanel.isAttachedToWindow()).thenReturn(true);
when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout);
@@ -107,11 +107,13 @@
.thenReturn(mBrightnessSlider);
when(mBrightnessControllerFactory.create(any(ToggleSlider.class)))
.thenReturn(mBrightnessController);
- when(mQSTileRevealControllerFactory.create(any())).thenReturn(mQSTileRevealController);
+ when(mQSTileRevealControllerFactory.create(any(), any()))
+ .thenReturn(mQSTileRevealController);
+ when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
mController = new QSPanelController(mQSPanel, mQSSecurityFooter, mTunerService,
- mQSTileHost, mQSCustomizerController, mQSTileRevealControllerFactory, mDumpManager,
- mMetricsLogger, mUiEventLogger, mBrightnessControllerFactory,
+ mQSTileHost, mQSCustomizerController, mMediaHost, mQSTileRevealControllerFactory,
+ mDumpManager, mMetricsLogger, mUiEventLogger, mBrightnessControllerFactory,
mToggleSliderViewControllerFactory);
mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index c82aee4..6fa6f31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -23,8 +23,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -38,16 +39,18 @@
import android.view.ViewGroup;
import android.widget.TextView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.SecurityController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
/*
* Compile and run the whole SystemUI test suite:
@@ -73,22 +76,23 @@
private TextView mFooterText;
private TestableImageView mFooterIcon;
private QSSecurityFooter mFooter;
- private SecurityController mSecurityController = mock(SecurityController.class);
+ @Mock
+ private SecurityController mSecurityController;
+ @Mock
private UserTracker mUserTracker;
+ @Mock
+ private ActivityStarter mActivityStarter;
@Before
public void setUp() {
- mDependency.injectTestDependency(SecurityController.class, mSecurityController);
- mDependency.injectTestDependency(Dependency.BG_LOOPER,
- TestableLooper.get(this).getLooper());
- mUserTracker = mock(UserTracker.class);
+ MockitoAnnotations.initMocks(this);
+ Looper looper = TestableLooper.get(this).getLooper();
when(mUserTracker.getUserInfo()).thenReturn(mock(UserInfo.class));
- mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
- new LayoutInflaterBuilder(mContext)
- .replace("ImageView", TestableImageView.class)
- .build());
- mFooter = new QSSecurityFooter(null, mContext, mUserTracker);
- mRootView = (ViewGroup) mFooter.getView();
+ mRootView = (ViewGroup) new LayoutInflaterBuilder(mContext)
+ .replace("ImageView", TestableImageView.class)
+ .build().inflate(R.layout.quick_settings_footer, null, false);
+ mFooter = new QSSecurityFooter(mRootView, mContext, mUserTracker, new Handler(looper),
+ mActivityStarter, mSecurityController, looper);
mFooterText = mRootView.findViewById(R.id.footer_text);
mFooterIcon = mRootView.findViewById(R.id.footer_icon);
mFooter.setHostEnvironment(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 26b5d26..d1cc757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -21,6 +21,7 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoModeController
@@ -89,6 +90,8 @@
@Mock
private lateinit var privacyLogger: PrivacyLogger
@Mock
+ private lateinit var colorExtractor: SysuiColorExtractor
+ @Mock
private lateinit var iconContainer: StatusIconContainer
@Mock
private lateinit var qsCarrierGroup: QSCarrierGroup
@@ -127,7 +130,8 @@
userTracker,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
- privacyLogger
+ privacyLogger,
+ colorExtractor
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
index c113df0b..1bfe10c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar.notification.collection;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
import static org.junit.Assert.assertNotNull;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -72,6 +76,17 @@
}
}
+ public NotificationChannel createNotificationChannel(String id, String name) {
+ return new NotificationChannel(id, name, IMPORTANCE_DEFAULT);
+ }
+
+ public void issueChannelModification(
+ String pkg, UserHandle user, NotificationChannel channel, int modificationType) {
+ for (NotificationHandler listener : mListeners) {
+ listener.onNotificationChannelModified(pkg, user, channel, modificationType);
+ }
+ }
+
public void setRanking(String key, Ranking ranking) {
mRankings.put(key, ranking);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 34889ff..a658469 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellDump;
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedGestureHandler;
@@ -68,6 +69,7 @@
@Mock HideDisplayCutout mHideDisplayCutout;
@Mock ProtoTracer mProtoTracer;
@Mock ShellDump mShellDump;
+ @Mock AppPairs mAppPairs;
@Before
public void setUp() {
@@ -77,7 +79,7 @@
mKeyguardUpdateMonitor, mNavigationModeController,
mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer,
- Optional.of(mShellDump));
+ Optional.of(mShellDump), Optional.of(mAppPairs));
when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 1c4d752..f1988e9 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -95,6 +95,8 @@
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TypedValue;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -3075,8 +3077,7 @@
int N;
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "gs");
out.attribute(null, "version", String.valueOf(CURRENT_VERSION));
@@ -3138,8 +3139,7 @@
List<LoadedWidgetState> outLoadedWidgets) {
int version = -1;
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int legacyProviderIndex = -1;
int legacyHostIndex = -1;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1eba37d..d586f00 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -137,6 +137,7 @@
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
+import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -189,7 +190,6 @@
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
-import com.android.modules.utils.BasicShellCommandHandler;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.server.am.BatteryStatsService;
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index f079546..735d248 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -40,6 +40,8 @@
import android.util.LongArrayQueue;
import android.util.MathUtils;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -934,8 +936,7 @@
mAllObservers.clear();
try {
infile = mPolicyFile.openRead();
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(infile, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(infile);
XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG);
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -1027,8 +1028,7 @@
}
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, TAG_PACKAGE_WATCHDOG);
out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index edeb049..2455e76 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -30,6 +30,8 @@
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -101,9 +103,8 @@
mEnabled = enable;
FileOutputStream outputStream = null;
try {
- XmlSerializer serializer = new FastXmlSerializer();
outputStream = mAtomicFile.startWrite();
- serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
serializer.startDocument(null, true);
serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(enable));
@@ -153,8 +154,7 @@
}
boolean enabled;
try (FileInputStream inputStream = mAtomicFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
parser.next();
String tagName = parser.getName();
@@ -176,9 +176,8 @@
synchronized (mLock) {
FileOutputStream outputStream = null;
try {
- XmlSerializer serializer = new FastXmlSerializer();
outputStream = mAtomicFile.startWrite();
- serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
serializer.startDocument(null, true);
serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(mEnabled));
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index ab933a8..783866a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -130,6 +130,8 @@
import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -2023,8 +2025,7 @@
FileInputStream fis = null;
try {
fis = mSettingsFile.openRead();
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser in = Xml.resolvePullParser(fis);
int type;
while ((type = in.next()) != END_DOCUMENT) {
@@ -2063,8 +2064,7 @@
try {
fos = mSettingsFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_VOLUMES);
writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java
index 6c1ffdd..61a7d00 100644
--- a/services/core/java/com/android/server/SystemUpdateManagerService.java
+++ b/services/core/java/com/android/server/SystemUpdateManagerService.java
@@ -38,6 +38,8 @@
import android.provider.Settings;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -130,8 +132,7 @@
private Bundle loadSystemUpdateInfoLocked() {
PersistableBundle loadedBundle = null;
try (FileInputStream fis = mFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(fis);
loadedBundle = readInfoFileLocked(parser);
} catch (FileNotFoundException e) {
Slog.i(TAG, "No existing info file " + mFile.getBaseFile());
@@ -216,8 +217,7 @@
try {
fos = mFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_INFO);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 770e8de..eb55512 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -78,6 +78,7 @@
import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Pair;
@@ -2564,12 +2565,33 @@
TelephonyUtils.dataStateToString(pdcs.getState()));
intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, pdcs.getApnSetting().getApnName());
intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY,
- ApnSetting.getApnTypesStringFromBitmask(pdcs.getApnSetting().getApnTypeBitmask()));
+ getApnTypesStringFromBitmask(pdcs.getApnSetting().getApnTypeBitmask()));
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, slotIndex);
intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
}
+ /**
+ * Reimplementation of {@link ApnSetting#getApnTypesStringFromBitmask}.
+ */
+ @VisibleForTesting
+ public static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
+ List<String> types = new ArrayList<>();
+ int remainingApnTypes = apnTypeBitmask;
+ // special case for DEFAULT since it's not a pure bit
+ if ((remainingApnTypes & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
+ types.add(ApnSetting.TYPE_DEFAULT_STRING);
+ remainingApnTypes &= ~ApnSetting.TYPE_DEFAULT;
+ }
+ while (remainingApnTypes != 0) {
+ int highestApnTypeBit = Integer.highestOneBit(remainingApnTypes);
+ String apnString = ApnSetting.getApnTypeString(highestApnTypeBit);
+ if (!TextUtils.isEmpty(apnString)) types.add(apnString);
+ remainingApnTypes &= ~highestApnTypeBit;
+ }
+ return TextUtils.join(",", types);
+ }
+
private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
if (checkNotifyPermission()) {
return;
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index ed83a64..f59af76 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -64,6 +64,8 @@
import android.util.AtomicFile;
import android.util.Base64;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.R;
@@ -1935,8 +1937,7 @@
return keyMap;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
// Check for supported keystore version.
XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
if (parser.next() != XmlPullParser.END_DOCUMENT) {
@@ -2007,8 +2008,7 @@
return keyMap;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
@@ -2058,8 +2058,7 @@
return trustedNetworks;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
// Check for supported keystore version.
XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
if (parser.next() != XmlPullParser.END_DOCUMENT) {
@@ -2144,9 +2143,8 @@
}
FileOutputStream keyStream = null;
try {
- XmlSerializer serializer = new FastXmlSerializer();
keyStream = mAtomicKeyFile.startWrite();
- serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(keyStream);
serializer.startDocument(null, true);
serializer.startTag(null, XML_KEYSTORE_START_TAG);
diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java
index d7e95df..7691852 100644
--- a/services/core/java/com/android/server/adb/AdbShellCommand.java
+++ b/services/core/java/com/android/server/adb/AdbShellCommand.java
@@ -16,7 +16,7 @@
package com.android.server.adb;
-import com.android.modules.utils.BasicShellCommandHandler;
+import android.os.BasicShellCommandHandler;
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index db63638..43474d5 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -70,6 +70,9 @@
// The uid of the process who started this instrumentation.
int mSourceUid;
+ // True if instrumentation should take place without restarting the target process.
+ boolean mNoRestart;
+
ActiveInstrumentation(ActivityManagerService service) {
mService = service;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 796ea0e..4ca1358 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -14218,9 +14219,12 @@
synchronized(this) {
InstrumentationInfo ii = null;
ApplicationInfo ai = null;
+
+ boolean noRestart = (flags & INSTR_FLAG_NO_RESTART) != 0;
+
try {
ii = mContext.getPackageManager().getInstrumentationInfo(
- className, STOCK_PM_FLAGS);
+ className, STOCK_PM_FLAGS);
ai = AppGlobals.getPackageManager().getApplicationInfo(
ii.targetPackage, STOCK_PM_FLAGS, userId);
} catch (PackageManager.NameNotFoundException e) {
@@ -14236,24 +14240,33 @@
"Unable to find instrumentation target package: " + ii.targetPackage);
return false;
}
- if (!ai.hasCode()) {
+
+ if (ii.targetPackage.equals("android")) {
+ if (!noRestart) {
+ reportStartInstrumentationFailureLocked(watcher, className,
+ "Cannot instrument system server without 'no-restart'");
+ return false;
+ }
+ } else if (!ai.hasCode()) {
reportStartInstrumentationFailureLocked(watcher, className,
"Instrumentation target has no code: " + ii.targetPackage);
return false;
}
- int match = mContext.getPackageManager().checkSignatures(
- ii.targetPackage, ii.packageName);
- if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
- String msg = "Permission Denial: starting instrumentation "
- + className + " from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingPid()
- + " not allowed because package " + ii.packageName
- + " does not have a signature matching the target "
- + ii.targetPackage;
- reportStartInstrumentationFailureLocked(watcher, className, msg);
- throw new SecurityException(msg);
+ if (!Build.IS_DEBUGGABLE) {
+ int match = mContext.getPackageManager().checkSignatures(
+ ii.targetPackage, ii.packageName);
+ if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+ String msg = "Permission Denial: starting instrumentation "
+ + className + " from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingPid()
+ + " not allowed because package " + ii.packageName
+ + " does not have a signature matching the target "
+ + ii.targetPackage;
+ reportStartInstrumentationFailureLocked(watcher, className, msg);
+ throw new SecurityException(msg);
+ }
}
ActiveInstrumentation activeInstr = new ActiveInstrumentation(this);
@@ -14276,6 +14289,7 @@
activeInstr.mHasBackgroundActivityStartsPermission = checkPermission(
START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
+ activeInstr.mNoRestart = noRestart;
boolean disableHiddenApiChecks = ai.usesNonSdkApi()
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
@@ -14291,18 +14305,25 @@
&& (flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0;
final long origId = Binder.clearCallingIdentity();
- // Instrumentation can kill and relaunch even persistent processes
- forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
- "start instr");
- // Inform usage stats to make the target package active
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(ii.targetPackage, userId,
- UsageEvents.Event.SYSTEM_INTERACTION);
+
+ ProcessRecord app;
+ if (noRestart) {
+ app = getProcessRecordLocked(ai.processName, ai.uid, true);
+ } else {
+ // Instrumentation can kill and relaunch even persistent processes
+ forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
+ "start instr");
+ // Inform usage stats to make the target package active
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(ii.targetPackage, userId,
+ UsageEvents.Event.SYSTEM_INTERACTION);
+ }
+ app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+ disableTestApiChecks, mountExtStorageFull, abiOverride,
+ ZYGOTE_POLICY_FLAG_EMPTY);
}
- ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, mountExtStorageFull, abiOverride,
- ZYGOTE_POLICY_FLAG_EMPTY);
+
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mSourceUid = callingUid;
@@ -14318,11 +14339,34 @@
ii.packageName, AppOpsManager.MODE_ALLOWED);
}
Binder.restoreCallingIdentity(origId);
+
+ if (noRestart) {
+ instrumentWithoutRestart(activeInstr, ai);
+ }
}
return true;
}
+ private void instrumentWithoutRestart(ActiveInstrumentation activeInstr,
+ ApplicationInfo targetInfo) {
+ ProcessRecord pr;
+ synchronized (this) {
+ pr = getProcessRecordLocked(targetInfo.processName, targetInfo.uid, true);
+ }
+
+ try {
+ pr.thread.instrumentWithoutRestart(
+ activeInstr.mClass,
+ activeInstr.mArguments,
+ activeInstr.mWatcher,
+ activeInstr.mUiAutomationConnection,
+ targetInfo);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "RemoteException from instrumentWithoutRestart", e);
+ }
+ }
+
private boolean isCallerShell() {
final int callingUid = Binder.getCallingUid();
return callingUid == SHELL_UID || callingUid == ROOT_UID;
@@ -14421,8 +14465,11 @@
instr.removeProcess(app);
app.setActiveInstrumentation(null);
- forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId,
- "finished inst");
+ if (!instr.mNoRestart) {
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
+ app.userId,
+ "finished inst");
+ }
}
public void finishInstrumentation(IApplicationThread target,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 0b2628d..09937e3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -141,6 +141,8 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -4291,8 +4293,7 @@
boolean success = false;
mUidStates.clear();
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
@@ -4550,8 +4551,7 @@
List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "app-ops");
out.attribute(null, "v", String.valueOf(CURRENT_VERSION));
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 3d22a15..f49b5dc 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -669,10 +669,12 @@
void shutdown() {
synchronized (mInMemoryLock) {
- if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) {
- persistPendingHistory();
+ if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
+ return;
}
}
+ // Do not call persistPendingHistory inside the memory lock, due to possible deadlock
+ persistPendingHistory();
}
void persistPendingHistory() {
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 3c18cd4..637a896 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -17,9 +17,11 @@
package com.android.server.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -33,6 +35,8 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.IBinder;
import android.os.RemoteException;
import android.security.KeyStore;
@@ -68,47 +72,54 @@
*/
static final int STATE_AUTH_CALLED = 1;
/**
- * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+ * Authentication started, BiometricPrompt is showing and the hardware is authenticating. At
+ * this point, the BiometricPrompt UI has been requested, but is not necessarily done animating
+ * in yet.
*/
static final int STATE_AUTH_STARTED = 2;
/**
+ * Same as {@link #STATE_AUTH_STARTED}, except the BiometricPrompt UI is done animating in.
+ */
+ static final int STATE_AUTH_STARTED_UI_SHOWING = 3;
+ /**
* Authentication is paused, waiting for the user to press "try again" button. Only
* passive modalities such as Face or Iris should have this state. Note that for passive
* modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
* fingerprint.
*/
- static final int STATE_AUTH_PAUSED = 3;
+ static final int STATE_AUTH_PAUSED = 4;
/**
* Paused, but "try again" was pressed. Sensors have new cookies and we're now waiting for all
* cookies to be returned.
*/
- static final int STATE_AUTH_PAUSED_RESUMING = 4;
+ static final int STATE_AUTH_PAUSED_RESUMING = 5;
/**
* Authentication is successful, but we're waiting for the user to press "confirm" button.
*/
- static final int STATE_AUTH_PENDING_CONFIRM = 5;
+ static final int STATE_AUTH_PENDING_CONFIRM = 6;
/**
* Biometric authenticated, waiting for SysUI to finish animation
*/
- static final int STATE_AUTHENTICATED_PENDING_SYSUI = 6;
+ static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
/**
* Biometric error, waiting for SysUI to finish animation
*/
- static final int STATE_ERROR_PENDING_SYSUI = 7;
+ static final int STATE_ERROR_PENDING_SYSUI = 8;
/**
* Device credential in AuthController is showing
*/
- static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8;
+ static final int STATE_SHOWING_DEVICE_CREDENTIAL = 9;
/**
* The client binder died, and sensors were authenticating at the time. Cancel has been
* requested and we're waiting for the HAL(s) to send ERROR_CANCELED.
*/
- static final int STATE_CLIENT_DIED_CANCELLING = 9;
+ static final int STATE_CLIENT_DIED_CANCELLING = 10;
@IntDef({
STATE_AUTH_IDLE,
STATE_AUTH_CALLED,
STATE_AUTH_STARTED,
+ STATE_AUTH_STARTED_UI_SHOWING,
STATE_AUTH_PAUSED,
STATE_AUTH_PAUSED_RESUMING,
STATE_AUTH_PENDING_CONFIRM,
@@ -150,6 +161,7 @@
private final int mCallingPid;
private final int mCallingUserId;
private final boolean mDebugEnabled;
+ private final List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
// The current state, which can be either idle, called, or started
private @SessionState int mState = STATE_AUTH_IDLE;
@@ -165,12 +177,25 @@
// Timestamp when hardware authentication occurred
private long mAuthenticatedTimeMs;
- AuthSession(Context context, IStatusBarService statusBarService,
- IBiometricSysuiReceiver sysuiReceiver, KeyStore keystore, Random random,
- ClientDeathReceiver clientDeathReceiver, PreAuthInfo preAuthInfo, IBinder token,
- long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
- IBiometricServiceReceiver clientReceiver, String opPackageName, PromptInfo promptInfo,
- int callingUid, int callingPid, int callingUserId, boolean debugEnabled) {
+ AuthSession(@NonNull Context context,
+ @NonNull IStatusBarService statusBarService,
+ @NonNull IBiometricSysuiReceiver sysuiReceiver,
+ @NonNull KeyStore keystore,
+ @NonNull Random random,
+ @NonNull ClientDeathReceiver clientDeathReceiver,
+ @NonNull PreAuthInfo preAuthInfo,
+ @NonNull IBinder token,
+ long operationId,
+ int userId,
+ @NonNull IBiometricSensorReceiver sensorReceiver,
+ @NonNull IBiometricServiceReceiver clientReceiver,
+ @NonNull String opPackageName,
+ @NonNull PromptInfo promptInfo,
+ int callingUid,
+ int callingPid,
+ int callingUserId,
+ boolean debugEnabled,
+ @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) {
mContext = context;
mStatusBarService = statusBarService;
mSysuiReceiver = sysuiReceiver;
@@ -189,6 +214,7 @@
mCallingPid = callingPid;
mCallingUserId = callingUserId;
mDebugEnabled = debugEnabled;
+ mFingerprintSensorProperties = fingerprintSensorProperties;
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -267,7 +293,10 @@
if (allCookiesReceived()) {
mStartTimeMs = System.currentTimeMillis();
- startAllPreparedSensors();
+
+ // For UDFPS, do not start until BiometricPrompt UI is shown. Otherwise, the UDFPS
+ // affordance will be shown before the BP UI is finished animating in.
+ startAllPreparedSensorsExceptUdfps();
// No need to request the UI if we're coming from the paused state.
if (mState != STATE_AUTH_PAUSED_RESUMING) {
@@ -311,13 +340,41 @@
return false;
}
- private void startAllPreparedSensors() {
+ private boolean isUdfpsSensor(@NonNull BiometricSensor sensor) {
+ if (sensor.modality != TYPE_FINGERPRINT) {
+ return false;
+ }
+
+ for (FingerprintSensorPropertiesInternal prop : mFingerprintSensorProperties) {
+ if (sensor.id == prop.sensorId && prop.isAnyUdfpsType()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void startAllPreparedSensorsExceptUdfps() {
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
+ if (isUdfpsSensor(sensor)) {
+ Slog.d(TAG, "Skipping UDFPS, sensorId: " + sensor.id);
+ continue;
+ }
try {
sensor.startSensor();
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to start prepared client, sensor ID: "
- + sensor.id, e);
+ Slog.e(TAG, "Unable to start prepared client, sensor: " + sensor, e);
+ }
+ }
+ }
+
+ private void startPreparedUdfpsSensors() {
+ for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
+ if (isUdfpsSensor(sensor)) {
+ try {
+ sensor.startSensor();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start UDFPS sensor: " + sensor, e);
+ }
}
}
}
@@ -390,7 +447,8 @@
break;
}
- case STATE_AUTH_STARTED: {
+ case STATE_AUTH_STARTED:
+ case STATE_AUTH_STARTED_UI_SHOWING: {
final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
|| error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
if (isAllowDeviceCredential() && errorLockout) {
@@ -463,6 +521,17 @@
}
}
+ void onDialogAnimatedIn() {
+ if (mState != STATE_AUTH_STARTED) {
+ Slog.w(TAG, "onDialogAnimatedIn, unexpected state: " + mState);
+ }
+
+ mState = STATE_AUTH_STARTED_UI_SHOWING;
+
+ // For UDFPS devices, we can now start the sensor.
+ startPreparedUdfpsSensors();
+ }
+
void onTryAgainPressed() {
if (mState != STATE_AUTH_PAUSED) {
Slog.w(TAG, "onTryAgainPressed, state: " + mState);
@@ -543,13 +612,15 @@
*/
boolean onClientDied() {
try {
- if (mState == STATE_AUTH_STARTED) {
- mState = STATE_CLIENT_DIED_CANCELLING;
- cancelAllSensors();
- return false;
- } else {
- mStatusBarService.hideAuthenticationDialog();
- return true;
+ switch (mState) {
+ case STATE_AUTH_STARTED:
+ case STATE_AUTH_STARTED_UI_SHOWING:
+ mState = STATE_CLIENT_DIED_CANCELLING;
+ cancelAllSensors();
+ return false;
+ default:
+ mStatusBarService.hideAuthenticationDialog();
+ return true;
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote Exception: " + e);
@@ -676,7 +747,10 @@
* @return true if this AuthSession is finished, e.g. should be set to null
*/
boolean onCancelAuthSession(boolean force) {
- if (mState == STATE_AUTH_STARTED && !force) {
+ final boolean authStarted = mState == STATE_AUTH_STARTED
+ || mState == STATE_AUTH_STARTED_UI_SHOWING;
+
+ if (authStarted && !force) {
cancelAllSensors();
// Wait for ERROR_CANCELED to be returned from the sensors
return false;
@@ -705,7 +779,7 @@
* {@link #STATE_SHOWING_DEVICE_CREDENTIAL} or dismissed.
*/
private void cancelBiometricOnly() {
- if (mState == STATE_AUTH_STARTED) {
+ if (mState == STATE_AUTH_STARTED || mState == STATE_AUTH_STARTED_UI_SHOWING) {
cancelAllSensors();
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 29424b4..196582a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -39,6 +39,8 @@
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.DeadObjectException;
@@ -90,6 +92,7 @@
private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
private static final int MSG_ON_SYSTEM_EVENT = 13;
private static final int MSG_CLIENT_DIED = 14;
+ private static final int MSG_ON_DIALOG_ANIMATED_IN = 15;
private final Injector mInjector;
private final DevicePolicyManager mDevicePolicyManager;
@@ -221,6 +224,11 @@
break;
}
+ case MSG_ON_DIALOG_ANIMATED_IN: {
+ handleOnDialogAnimatedIn();
+ break;
+ }
+
default:
Slog.e(TAG, "Unknown message: " + msg);
break;
@@ -451,6 +459,11 @@
public void onSystemEvent(int event) {
mHandler.obtainMessage(MSG_ON_SYSTEM_EVENT, event).sendToTarget();
}
+
+ @Override
+ public void onDialogAnimatedIn() {
+ mHandler.obtainMessage(MSG_ON_DIALOG_ANIMATED_IN).sendToTarget();
+ }
};
private final AuthSession.ClientDeathReceiver mClientDeathReceiver = () -> {
@@ -749,6 +762,16 @@
public DevicePolicyManager getDevicePolicyManager(Context context) {
return context.getSystemService(DevicePolicyManager.class);
}
+
+ public List<FingerprintSensorPropertiesInternal> getFingerprintSensorProperties(
+ Context context) {
+ final FingerprintManager fpm = context.getSystemService(FingerprintManager.class);
+ if (fpm != null) {
+ return fpm.getSensorPropertiesInternal();
+ } else {
+ return new ArrayList<>();
+ }
+ }
}
/**
@@ -932,7 +955,7 @@
private void handleClientDied() {
if (mCurrentAuthSession == null) {
- Slog.e(TAG, "Auth session null");
+ Slog.e(TAG, "handleClientDied: AuthSession is null");
return;
}
@@ -943,6 +966,15 @@
}
}
+ private void handleOnDialogAnimatedIn() {
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "handleOnDialogAnimatedIn: AuthSession is null");
+ return;
+ }
+
+ mCurrentAuthSession.onDialogAnimatedIn();
+ }
+
/**
* Invoked when each service has notified that its client is ready to be started. When
* all biometrics are ready, this invokes the SystemUI dialog through StatusBar.
@@ -1026,7 +1058,8 @@
mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver,
mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, operationId, userId,
mBiometricSensorReceiver, receiver, opPackageName, promptInfo, callingUid,
- callingPid, callingUserId, debugEnabled);
+ callingPid, callingUserId, debugEnabled,
+ mInjector.getFingerprintSensorProperties(getContext()));
try {
mCurrentAuthSession.goToInitialState();
} catch (RemoteException e) {
@@ -1053,5 +1086,12 @@
pw.println(" " + sensor);
}
pw.println("CurrentSession: " + mCurrentAuthSession);
+
+ final List<FingerprintSensorPropertiesInternal> fpProps =
+ mInjector.getFingerprintSensorProperties(getContext());
+ pw.println("FingerprintSensorProperties: " + fpProps.size());
+ for (FingerprintSensorPropertiesInternal prop : fpProps) {
+ pw.println(" " + prop);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index ce2d340..2784f46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.os.Handler;
import android.os.IBinder;
@@ -378,9 +379,20 @@
return;
}
if (mCurrentOperation.state != Operation.STATE_WAITING_FOR_COOKIE) {
- Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
- + ", expected STATE_WAITING_FOR_COOKIE");
- return;
+ if (mCurrentOperation.state == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
+ + mCurrentOperation);
+ // This should trigger the internal onClientFinished callback, which clears the
+ // operation and starts the next one.
+ final Interruptable interruptable = (Interruptable) mCurrentOperation.clientMonitor;
+ interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ return;
+ } else {
+ Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
+ + ", expected STATE_WAITING_FOR_COOKIE");
+ return;
+ }
}
if (mCurrentOperation.clientMonitor.getCookie() != cookie) {
Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
@@ -461,6 +473,13 @@
Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
return;
}
+ if (operation.state == Operation.STATE_WAITING_FOR_COOKIE) {
+ Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
+ // We can set it to null immediately, since the HAL was never notified to start.
+ mCurrentOperation = null;
+ startNextOperationIfIdle();
+ return;
+ }
Slog.d(getTag(), "[Cancelling] Current client: " + operation.clientMonitor);
final Interruptable interruptable = (Interruptable) operation.clientMonitor;
interruptable.cancel();
@@ -505,8 +524,9 @@
mCurrentOperation.clientMonitor instanceof AuthenticationConsumer;
final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
if (!isAuthenticating || !tokenMatches) {
- Slog.w(getTag(), "Not cancelling authentication, isEnrolling: " + isAuthenticating
- + " tokenMatches: " + tokenMatches);
+ Slog.w(getTag(), "Not cancelling authentication"
+ + ", current operation : " + mCurrentOperation
+ + ", tokenMatches: " + tokenMatches);
return;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index c87f62f..61e7c89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -37,7 +37,7 @@
* It may be possible at some point in the future to combine I<Sensor>ServiceReceivers to share
* a common interface.
*/
-public final class ClientMonitorCallbackConverter {
+public class ClientMonitorCallbackConverter {
private IBiometricSensorReceiver mSensorReceiver; // BiometricService
private IFaceServiceReceiver mFaceServiceReceiver; // FaceManager
private IFingerprintServiceReceiver mFingerprintServiceReceiver; // FingerprintManager
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index e9c4b51..9a1f1e5 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -50,6 +50,8 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -1635,8 +1637,7 @@
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
Slog.v(TAG_FILE, "Reading " + mAccountInfoFile.getBaseFile());
}
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
eventType != XmlPullParser.END_DOCUMENT) {
@@ -1988,8 +1989,7 @@
try {
fos = mAccountInfoFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
index 6e571bd..928799b 100644
--- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -22,6 +22,8 @@
import android.os.SystemClock;
import android.os.UserManager;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -165,8 +167,7 @@
}
public void writeToXML(OutputStream stream) throws IOException {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -206,8 +207,7 @@
public void readFromXML(InputStream stream) throws IOException {
try {
Map<Integer, Deque<AmbientBrightnessDayStats>> parsedStats = new HashMap<>();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 9e82d4f..3ae99ef 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -55,6 +55,8 @@
import android.provider.Settings;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Display;
@@ -536,8 +538,7 @@
@VisibleForTesting
@GuardedBy("mEventsLock")
void writeEventsLocked(OutputStream stream) throws IOException {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -607,8 +608,7 @@
@GuardedBy("mEventsLock")
void readEventsLocked(InputStream stream) throws IOException {
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3ac2185..004e481 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -89,6 +89,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.Spline;
import android.view.Display;
import android.view.DisplayInfo;
@@ -96,6 +97,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -112,7 +114,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
@@ -235,15 +236,28 @@
private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
@Override
public void requestDisplayState(int displayId, int state, float brightness) {
+ // TODO (b/168210494): Stop applying default display state to all displays.
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return;
+ }
+ final int[] displayIds;
+ synchronized (mSyncRoot) {
+ displayIds = mLogicalDisplayMapper.getDisplayIdsLocked();
+ }
+
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
- requestGlobalDisplayStateInternal(state, brightness);
+ for (int id : displayIds) {
+ requestDisplayStateInternal(id, state, brightness);
+ }
}
mDisplayPowerCallbacks.onDisplayStateChange(state);
if (state != Display.STATE_OFF) {
- requestGlobalDisplayStateInternal(state, brightness);
+ for (int id : displayIds) {
+ requestDisplayStateInternal(id, state, brightness);
+ }
}
}
};
@@ -257,13 +271,16 @@
/** The {@link Handler} used by all {@link DisplayPowerController}s. */
private Handler mPowerHandler;
- // The overall display state, independent of changes that might influence one
- // display or another in particular.
- private int mGlobalDisplayState = Display.STATE_ON;
+ // A map from LogicalDisplay ID to display power state.
+ @GuardedBy("mSyncRoot")
+ private final SparseIntArray mDisplayStates = new SparseIntArray();
- // The overall display brightness.
- // For now, this only applies to the default display but we may split it up eventually.
- private float mGlobalDisplayBrightness;
+ // A map from LogicalDisplay ID to display brightness.
+ @GuardedBy("mSyncRoot")
+ private final SparseArray<Float> mDisplayBrightnesses = new SparseArray<>();
+
+ // The default brightness.
+ private final float mDisplayDefaultBrightness;
// Set to true when there are pending display changes that have yet to be applied
// to the surface flinger state.
@@ -317,11 +334,6 @@
// DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY flag set.
private final int mDefaultDisplayDefaultColorMode;
- // Temporary list of deferred work to perform when setting the display state.
- // Only used by requestDisplayState. The field is self-synchronized and only
- // intended for use inside of the requestGlobalDisplayStateInternal function.
- private final ArrayList<Runnable> mTempDisplayStateWorkQueue = new ArrayList<Runnable>();
-
// Lists of UIDs that are present on the displays. Maps displayId -> array of UIDs.
private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
@@ -372,7 +384,7 @@
mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
PowerManager pm = mContext.getSystemService(PowerManager.class);
- mGlobalDisplayBrightness = pm.getBrightnessConstraint(
+ mDisplayDefaultBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
mCurrentUserId = UserHandle.USER_SYSTEM;
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
@@ -589,7 +601,7 @@
}
}
- private void requestGlobalDisplayStateInternal(int state, float brightnessState) {
+ private void requestDisplayStateInternal(int displayId, int state, float brightnessState) {
if (state == Display.STATE_UNKNOWN) {
state = Display.STATE_ON;
}
@@ -602,38 +614,40 @@
brightnessState = PowerManager.BRIGHTNESS_MAX;
}
- synchronized (mTempDisplayStateWorkQueue) {
- try {
- // Update the display state within the lock.
- // Note that we do not need to schedule traversals here although it
- // may happen as a side-effect of displays changing state.
- synchronized (mSyncRoot) {
- if (mGlobalDisplayState == state
- && mGlobalDisplayBrightness == brightnessState) {
- return; // no change
- }
+ // Update the display state within the lock.
+ // Note that we do not need to schedule traversals here although it
+ // may happen as a side-effect of displays changing state.
+ final Runnable runnable;
+ final String traceMessage;
+ synchronized (mSyncRoot) {
+ final int index = mDisplayStates.indexOfKey(displayId);
- Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState("
- + Display.stateToString(state)
- + ", brightness=" + brightnessState + ")");
-
- mGlobalDisplayState = state;
- mGlobalDisplayBrightness = brightnessState;
- applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
- }
-
- // Setting the display power state can take hundreds of milliseconds
- // to complete so we defer the most expensive part of the work until
- // after we have exited the critical section to avoid blocking other
- // threads for a long time.
- for (int i = 0; i < mTempDisplayStateWorkQueue.size(); i++) {
- mTempDisplayStateWorkQueue.get(i).run();
- }
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
- } finally {
- mTempDisplayStateWorkQueue.clear();
+ if (index < 0 || (mDisplayStates.valueAt(index) == state
+ && BrightnessSynchronizer.floatEquals(mDisplayBrightnesses.valueAt(index),
+ brightnessState))) {
+ return; // Display no longer exists or no change.
}
+
+ traceMessage = "requestDisplayStateInternal("
+ + displayId + ", "
+ + Display.stateToString(state)
+ + ", brightness=" + brightnessState + ")";
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, traceMessage, displayId);
+
+ mDisplayStates.setValueAt(index, state);
+ mDisplayBrightnesses.setValueAt(index, brightnessState);
+ runnable = updateDisplayStateLocked(
+ mLogicalDisplayMapper.getLocked(displayId).getPrimaryDisplayDeviceLocked());
}
+
+ // Setting the display power state can take hundreds of milliseconds
+ // to complete so we defer the most expensive part of the work until
+ // after we have exited the critical section to avoid blocking other
+ // threads for a long time.
+ if (runnable != null) {
+ runnable.run();
+ }
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, traceMessage, displayId);
}
private class SettingsObserver extends ContentObserver {
@@ -987,6 +1001,8 @@
recordTopInsetLocked(display);
}
addDisplayPowerControllerLocked(displayId);
+ mDisplayStates.append(displayId, Display.STATE_OFF);
+ mDisplayBrightnesses.append(displayId, mDisplayDefaultBrightness);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1021,6 +1037,8 @@
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
mDisplayPowerControllers.delete(displayId);
+ mDisplayStates.delete(displayId);
+ mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
scheduleTraversalLocked(false);
@@ -1035,15 +1053,6 @@
handleLogicalDisplayChangedLocked(display);
}
- private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
- mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
- Runnable runnable = updateDisplayStateLocked(device);
- if (runnable != null) {
- workQueue.add(runnable);
- }
- });
- }
-
private Runnable updateDisplayStateLocked(DisplayDevice device) {
// Blank or unblank the display immediately to match the state requested
// by the display power controller (if known).
@@ -1052,12 +1061,16 @@
// TODO - b/170498827 The rules regarding what display state to apply to each
// display will depend on the configuration/mapping of logical displays.
// Clean up LogicalDisplay.isEnabled() mechanism once this is fixed.
- int state = mGlobalDisplayState;
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
- if (display != null && !display.isEnabled()) {
+ final int state;
+ final int displayId = display.getDisplayIdLocked();
+ if (display.isEnabled()) {
+ state = mDisplayStates.get(displayId);
+ } else {
state = Display.STATE_OFF;
}
- return device.requestDisplayStateLocked(state, mGlobalDisplayBrightness);
+ final float brightness = mDisplayBrightnesses.get(displayId);
+ return device.requestDisplayStateLocked(state, brightness);
}
return null;
}
@@ -1587,13 +1600,24 @@
pw.println(" mOnlyCode=" + mOnlyCore);
pw.println(" mSafeMode=" + mSafeMode);
pw.println(" mPendingTraversal=" + mPendingTraversal);
- pw.println(" mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState));
pw.println(" mViewports=" + mViewports);
pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
pw.println(" mStableDisplaySize=" + mStableDisplaySize);
pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
+ pw.println();
+ final int displayStateCount = mDisplayStates.size();
+ pw.println("Display States: size=" + displayStateCount);
+ for (int i = 0; i < displayStateCount; i++) {
+ final int displayId = mDisplayStates.keyAt(i);
+ final int displayState = mDisplayStates.valueAt(i);
+ final float brightness = mDisplayBrightnesses.valueAt(i);
+ pw.println(" Display Id=" + displayId);
+ pw.println(" Display State=" + Display.stateToString(displayState));
+ pw.println(" Display Brightness=" + brightness);
+ }
+
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 09c9aab..f488260 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -685,8 +685,6 @@
}
private void initialize() {
- // Initialize the power state object for the default display.
- // In the future, we might manage multiple displays independently.
mPowerState = new DisplayPowerState(mBlanker,
mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index e35becc..979c3b8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -588,7 +588,7 @@
}
/**
- * Swap the underlying {@link DisplayDevice} with the specificed LogicalDisplay.
+ * Swap the underlying {@link DisplayDevice} with the specified LogicalDisplay.
*
* @param targetDisplay The display with which to swap display-devices.
* @return {@code true} if the displays were swapped, {@code false} otherwise.
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index a843af5..45c38b4 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.content.Context;
+import android.os.Process;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
@@ -149,6 +150,10 @@
return null;
}
+ public int[] getDisplayIdsLocked() {
+ return getDisplayIdsLocked(Process.SYSTEM_UID);
+ }
+
public int[] getDisplayIdsLocked(int callingUid) {
final int count = mLogicalDisplays.size();
int[] displayIds = new int[count];
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 09b0b3a..494dd39 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -25,6 +25,7 @@
import android.util.SparseArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Display;
@@ -323,8 +324,7 @@
XmlPullParser parser;
try {
- parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
+ parser = Xml.resolvePullParser(is);
loadFromXml(parser);
} catch (IOException ex) {
Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
@@ -343,8 +343,7 @@
os = mInjector.startWrite();
boolean success = false;
try {
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(os);
saveToXml(serializer);
serializer.flush();
success = true;
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 196787a..f61662d 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -29,6 +29,8 @@
import android.hardware.input.TouchCalibration;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import java.io.BufferedInputStream;
@@ -214,10 +216,9 @@
return;
}
- XmlPullParser parser;
+ TypedXmlPullParser parser;
try {
- parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
+ parser = Xml.resolvePullParser(is);
loadFromXml(parser);
} catch (IOException ex) {
Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
@@ -236,8 +237,7 @@
os = mAtomicFile.startWrite();
boolean success = false;
try {
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(os);
saveToXml(serializer);
serializer.flush();
success = true;
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index f79ac16..a077b04 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -25,6 +25,8 @@
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -139,8 +141,7 @@
final AtomicFile subtypesFile = getAdditionalSubtypeFile(inputMethodDir);
try {
fos = subtypesFile.startWrite();
- final XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, NODE_SUBTYPES);
@@ -207,8 +208,7 @@
return;
}
try (FileInputStream fis = subtypesFile.openRead()) {
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int type = parser.getEventType();
// Skip parsing until START_TAG
while (true) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 086757f..2bb2dcc 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1212,9 +1212,7 @@
ipw.println("Event Log:");
ipw.increaseIndent();
- for (String log : mInjector.getLocationEventLog()) {
- ipw.println(log);
- }
+ mInjector.getLocationEventLog().iterate(ipw::println);
ipw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 3464704..909873f 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -16,10 +16,9 @@
package com.android.server.location;
+import android.os.BasicShellCommandHandler;
import android.os.UserHandle;
-import com.android.modules.utils.BasicShellCommandHandler;
-
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/location/util/LocationEventLog.java b/services/core/java/com/android/server/location/util/LocationEventLog.java
index 134b142..0caab05 100644
--- a/services/core/java/com/android/server/location/util/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/util/LocationEventLog.java
@@ -59,46 +59,46 @@
}
/** Logs a location enabled/disabled event. */
- public synchronized void logLocationEnabled(int userId, boolean enabled) {
+ public void logLocationEnabled(int userId, boolean enabled) {
addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, userId, enabled);
}
/** Logs a location provider enabled/disabled event. */
- public synchronized void logProviderEnabled(String provider, int userId, boolean enabled) {
+ public void logProviderEnabled(String provider, int userId, boolean enabled) {
addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
}
/** Logs a location provider being replaced/unreplaced by a mock provider. */
- public synchronized void logProviderMocked(String provider, boolean mocked) {
+ public void logProviderMocked(String provider, boolean mocked) {
addLogEvent(EVENT_PROVIDER_MOCKED, provider, mocked);
}
/** Logs a new client registration for a location provider. */
- public synchronized void logProviderClientRegistered(String provider, CallerIdentity identity,
+ public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
}
/** Logs a client unregistration for a location provider. */
- public synchronized void logProviderClientUnregistered(String provider,
+ public void logProviderClientUnregistered(String provider,
CallerIdentity identity) {
addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
}
/** Logs a change to the provider request for a location provider. */
- public synchronized void logProviderUpdateRequest(String provider, ProviderRequest request) {
+ public void logProviderUpdateRequest(String provider, ProviderRequest request) {
addLogEvent(EVENT_PROVIDER_UPDATE_REQUEST, provider, request);
}
/** Logs a new incoming location for a location provider. */
- public synchronized void logProviderReceivedLocations(String provider, int numLocations) {
+ public void logProviderReceivedLocations(String provider, int numLocations) {
if (Build.IS_DEBUGGABLE || D) {
addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
}
}
/** Logs a location deliver for a client of a location provider. */
- public synchronized void logProviderDeliveredLocations(String provider, int numLocations,
+ public void logProviderDeliveredLocations(String provider, int numLocations,
CallerIdentity identity) {
if (Build.IS_DEBUGGABLE || D) {
addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
@@ -106,7 +106,7 @@
}
/** Logs that the location power save mode has changed. */
- public synchronized void logLocationPowerSaveMode(
+ public void logLocationPowerSaveMode(
@LocationPowerSaveMode int locationPowerSaveMode) {
addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, locationPowerSaveMode);
}
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
new file mode 100644
index 0000000..5fa2b1c
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+
+import java.io.PrintWriter;
+
+/**
+ * Util class for media server.
+ */
+class MediaServerUtils {
+ /**
+ * Verify that caller holds {@link android.Manifest.permission#DUMP}.
+ */
+ public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
+ if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump " + tag + " from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " due to missing android.permission.DUMP permission");
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d5ce8a6..c23bfc4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -83,9 +83,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
@@ -1885,7 +1883,7 @@
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ if (!MediaServerUtils.checkDumpPermission(mContext, TAG, pw)) return;
pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
pw.println();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index dd6c8f5..a44ddcb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -217,6 +217,8 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.R;
@@ -2226,8 +2228,7 @@
FileInputStream fis = null;
try {
fis = mPolicyFile.openRead();
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser in = Xml.resolvePullParser(fis);
// Must save the <restrict-background> tags and convert them to <uid-policy> later,
// to skip UIDs that were explicitly denylisted.
@@ -2496,8 +2497,7 @@
try {
fos = mPolicyFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_POLICY_LIST);
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
index c2f3ba0..4506b7d 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -20,6 +20,8 @@
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -92,8 +94,7 @@
return;
}
try (FileInputStream stream = mXmlFile.openRead()){
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
XmlUtils.beginDocument(parser, "network-watchlist-settings");
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -145,8 +146,7 @@
return;
}
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "network-watchlist-settings");
out.startTag(null, "secret-key");
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1516cde..1e2898b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -494,6 +494,10 @@
final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+ // True if the toast that's on top of the queue is being shown at the moment.
+ @GuardedBy("mToastQueue")
+ private boolean mIsCurrentToastShown = false;
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -7297,10 +7301,15 @@
@GuardedBy("mToastQueue")
void showNextToastLocked() {
+ if (mIsCurrentToastShown) {
+ return; // Don't show the same toast twice.
+ }
+
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (record.show()) {
scheduleDurationReachedLocked(record);
+ mIsCurrentToastShown = true;
return;
}
int index = mToastQueue.indexOf(record);
@@ -7316,6 +7325,10 @@
ToastRecord record = mToastQueue.get(index);
record.hide();
+ if (index == 0) {
+ mIsCurrentToastShown = false;
+ }
+
ToastRecord lastToast = mToastQueue.remove(index);
mWindowManagerInternal.removeWindowToken(lastToast.windowToken, false /* removeWindows */,
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 3d520bf..8a2d823 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -25,6 +25,8 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -405,10 +407,9 @@
public static void restore(@NonNull final ArrayList<SettingsItem> table,
@NonNull final InputStream is) throws IOException, XmlPullParserException {
- try (InputStreamReader reader = new InputStreamReader(is)) {
+ {
table.clear();
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(reader);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(is);
XmlUtils.beginDocument(parser, TAG_OVERLAYS);
int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
if (version != CURRENT_VERSION) {
@@ -465,8 +466,7 @@
public static void persist(@NonNull final ArrayList<SettingsItem> table,
@NonNull final OutputStream os) throws IOException, XmlPullParserException {
- final FastXmlSerializer xml = new FastXmlSerializer();
- xml.setOutput(os, "utf-8");
+ final TypedXmlSerializer xml = Xml.resolveSerializer(os);
xml.startDocument(null, true);
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
xml.startTag(null, TAG_OVERLAYS);
@@ -481,7 +481,7 @@
xml.endDocument();
}
- private static void persistRow(@NonNull final FastXmlSerializer xml,
+ private static void persistRow(@NonNull final TypedXmlSerializer xml,
@NonNull final SettingsItem item) throws IOException {
xml.startTag(null, TAG_ITEM);
XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName);
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index ff4049b..ea2439e 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -16,14 +16,19 @@
package com.android.server.pm;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-import android.content.IntentFilter;
-import android.util.Log;
+
import java.io.IOException;
-import android.os.UserHandle;
/**
* The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
@@ -63,7 +68,7 @@
return mOwnerPackage;
}
- CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
+ CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
mTargetUserId = getIntFromXml(parser, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
mFlags = getIntFromXml(parser, ATTR_FLAGS, 0);
@@ -98,7 +103,7 @@
}
}
- String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
+ String getStringFromXml(TypedXmlPullParser parser, String attribute, String defaultValue) {
String value = parser.getAttributeValue(null, attribute);
if (value == null) {
String msg = "Missing element under " + TAG +": " + attribute + " at " +
@@ -110,7 +115,7 @@
}
}
- int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
+ int getIntFromXml(TypedXmlPullParser parser, String attribute, int defaultValue) {
String stringValue = getStringFromXml(parser, attribute, null);
if (stringValue != null) {
return Integer.parseInt(stringValue);
@@ -118,7 +123,7 @@
return defaultValue;
}
- public void writeToXml(XmlSerializer serializer) throws IOException {
+ public void writeToXml(TypedXmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId));
serializer.attribute(null, ATTR_FLAGS, Integer.toString(mFlags));
serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index efbdfea..3f2c235 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -43,6 +43,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -1020,8 +1022,7 @@
final String packageName = instantDir.getName();
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(in);
return new UninstalledInstantAppState(
parseMetadata(parser, packageName), timestamp);
} catch (XmlPullParserException | IOException e) {
@@ -1061,7 +1062,7 @@
}
private static @Nullable
- InstantAppInfo parseMetadata(@NonNull XmlPullParser parser,
+ InstantAppInfo parseMetadata(@NonNull TypedXmlPullParser parser,
@NonNull String packageName)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
@@ -1073,7 +1074,7 @@
return null;
}
- private static InstantAppInfo parsePackage(@NonNull XmlPullParser parser,
+ private static InstantAppInfo parsePackage(@NonNull TypedXmlPullParser parser,
@NonNull String packageName)
throws IOException, XmlPullParserException {
String label = parser.getAttributeValue(null, ATTR_LABEL);
@@ -1098,7 +1099,7 @@
requestedPermissions, grantedPermissions);
}
- private static void parsePermissions(@NonNull XmlPullParser parser,
+ private static void parsePermissions(@NonNull TypedXmlPullParser parser,
@NonNull List<String> outRequestedPermissions,
@NonNull List<String> outGrantedPermissions)
throws IOException, XmlPullParserException {
@@ -1128,8 +1129,7 @@
try {
out = destination.startWrite();
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(out);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index dabcc35..86f024f 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -26,12 +26,13 @@
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
@@ -671,7 +672,7 @@
}
}
- void writeKeySetManagerServiceLPr(XmlSerializer serializer) throws IOException {
+ void writeKeySetManagerServiceLPr(TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, "keyset-settings");
serializer.attribute(null, "version", Integer.toString(CURRENT_VERSION));
writePublicKeysLPr(serializer);
@@ -685,7 +686,7 @@
serializer.endTag(null, "keyset-settings");
}
- void writePublicKeysLPr(XmlSerializer serializer) throws IOException {
+ void writePublicKeysLPr(TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, "keys");
for (int pKeyIndex = 0; pKeyIndex < mPublicKeys.size(); pKeyIndex++) {
long id = mPublicKeys.keyAt(pKeyIndex);
@@ -699,7 +700,7 @@
serializer.endTag(null, "keys");
}
- void writeKeySetsLPr(XmlSerializer serializer) throws IOException {
+ void writeKeySetsLPr(TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, "keysets");
for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
long id = mKeySetMapping.keyAt(keySetIndex);
@@ -716,7 +717,7 @@
serializer.endTag(null, "keysets");
}
- void readKeySetsLPw(XmlPullParser parser, ArrayMap<Long, Integer> keySetRefCounts)
+ void readKeySetsLPw(TypedXmlPullParser parser, ArrayMap<Long, Integer> keySetRefCounts)
throws XmlPullParserException, IOException {
int type;
long currentKeySetId = 0;
@@ -757,7 +758,7 @@
addRefCountsFromSavedPackagesLPw(keySetRefCounts);
}
- void readKeysLPw(XmlPullParser parser)
+ void readKeysLPw(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -773,7 +774,7 @@
}
}
- void readKeySetListLPw(XmlPullParser parser)
+ void readKeySetListLPw(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -798,7 +799,7 @@
}
}
- void readPublicKeyLPw(XmlPullParser parser)
+ void readPublicKeyLPw(TypedXmlPullParser parser)
throws XmlPullParserException {
String encodedID = parser.getAttributeValue(null, "identifier");
long identifier = Long.parseLong(encodedID);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c282b99..34019c7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -71,6 +71,8 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.R;
@@ -78,7 +80,6 @@
import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.ImageUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
@@ -91,9 +92,7 @@
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.CharArrayWriter;
import java.io.File;
@@ -102,7 +101,6 @@
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
@@ -383,8 +381,7 @@
FileInputStream fis = null;
try {
fis = mSessionsFile.openRead();
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser in = Xml.resolvePullParser(fis);
int type;
while ((type = in.next()) != END_DOCUMENT) {
@@ -467,8 +464,7 @@
try {
fos = mSessionsFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_SESSIONS);
final int size = mSessions.size();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 57b80ea..ab5b9d1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -131,6 +131,8 @@
import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.R;
@@ -3943,7 +3945,7 @@
}
}
- private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
+ private static void writeGrantedRuntimePermissionsLocked(TypedXmlSerializer out,
String[] grantedRuntimePermissions) throws IOException {
if (grantedRuntimePermissions != null) {
for (String permission : grantedRuntimePermissions) {
@@ -3954,7 +3956,7 @@
}
}
- private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull XmlSerializer out,
+ private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull TypedXmlSerializer out,
@Nullable List<String> whitelistedRestrictedPermissions) throws IOException {
if (whitelistedRestrictedPermissions != null) {
final int permissionCount = whitelistedRestrictedPermissions.size();
@@ -3966,7 +3968,7 @@
}
}
- private static void writeAutoRevokePermissionsMode(@NonNull XmlSerializer out, int mode)
+ private static void writeAutoRevokePermissionsMode(@NonNull TypedXmlSerializer out, int mode)
throws IOException {
out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
writeIntAttribute(out, ATTR_MODE, mode);
@@ -3979,12 +3981,12 @@
}
/**
- * Write this session to a {@link XmlSerializer}.
+ * Write this session to a {@link TypedXmlSerializer}.
*
* @param out Where to write the session to
* @param sessionsDir The directory containing the sessions
*/
- void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
+ void write(@NonNull TypedXmlSerializer out, @NonNull File sessionsDir) throws IOException {
synchronized (mLock) {
if (mDestroyed && !params.isStaged) {
return;
@@ -4127,7 +4129,7 @@
}
/**
- * Read new session from a {@link XmlPullParser xml description} and create it.
+ * Read new session from a {@link TypedXmlPullParser xml description} and create it.
*
* @param in The source of the description
* @param callback Callback the session uses to notify about changes of it's state
@@ -4139,7 +4141,7 @@
* @param sessionProvider
* @return The newly created session
*/
- public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
+ public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in,
@NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
@NonNull PackageManagerService pm, Looper installerThread,
@NonNull StagingManager stagingManager, @NonNull File sessionsDir,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0500616..09cdef8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -319,6 +319,8 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimingsTraceLog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.util.apk.ApkSignatureVerifier;
import android.util.jar.StrictJarFile;
@@ -20682,7 +20684,7 @@
* Common machinery for picking apart a restored XML blob and passing
* it to a caller-supplied functor to be applied to the running system.
*/
- private void restoreFromXml(XmlPullParser parser, int userId,
+ private void restoreFromXml(TypedXmlPullParser parser, int userId,
String expectedStartTag, BlobXmlRestorer functor)
throws IOException, XmlPullParserException {
int type;
@@ -20710,7 +20712,8 @@
}
private interface BlobXmlRestorer {
- void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
+ void apply(TypedXmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException;
}
/**
@@ -20726,7 +20729,7 @@
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
try {
- final XmlSerializer serializer = new FastXmlSerializer();
+ final TypedXmlSerializer serializer = Xml.newFastSerializer();
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.startTag(null, TAG_PREFERRED_BACKUP);
@@ -20755,7 +20758,7 @@
}
try {
- final XmlPullParser parser = Xml.newPullParser();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
(readParser, readUserId) -> {
@@ -20784,7 +20787,7 @@
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
try {
- final XmlSerializer serializer = new FastXmlSerializer();
+ final TypedXmlSerializer serializer = Xml.newFastSerializer();
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.startTag(null, TAG_DEFAULT_APPS);
@@ -20813,7 +20816,7 @@
}
try {
- final XmlPullParser parser = Xml.newPullParser();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
(parser1, userId1) -> {
@@ -20842,7 +20845,7 @@
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
try {
- final XmlSerializer serializer = new FastXmlSerializer();
+ final TypedXmlSerializer serializer = Xml.newFastSerializer();
serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION);
@@ -20871,7 +20874,7 @@
}
try {
- final XmlPullParser parser = Xml.newPullParser();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
(parser1, userId1) -> {
@@ -22634,7 +22637,7 @@
pw.flush();
FileOutputStream fout = new FileOutputStream(fd);
BufferedOutputStream str = new BufferedOutputStream(fout);
- XmlSerializer serializer = new FastXmlSerializer();
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
try {
serializer.setOutput(str, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index b7d1eec..e74792c 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -21,6 +21,8 @@
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -52,7 +54,7 @@
mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
}
- void writeXml(XmlSerializer serializer, String tagName,
+ void writeXml(TypedXmlSerializer serializer, String tagName,
ArrayList<Signature> writtenSignatures) throws IOException {
if (mSigningDetails.signatures == null) {
return;
@@ -75,8 +77,9 @@
serializer.endTag(null, tagName);
}
- private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures,
- Signature[] signatures, boolean isPastSigs) throws IOException {
+ private void writeCertsListXml(TypedXmlSerializer serializer,
+ ArrayList<Signature> writtenSignatures, Signature[] signatures, boolean isPastSigs)
+ throws IOException {
for (int i=0; i<signatures.length; i++) {
serializer.startTag(null, "cert");
final Signature sig = signatures[i];
@@ -104,7 +107,7 @@
}
}
- void readXml(XmlPullParser parser, ArrayList<Signature> readSignatures)
+ void readXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures)
throws IOException, XmlPullParserException {
PackageParser.SigningDetails.Builder builder =
new PackageParser.SigningDetails.Builder();
@@ -150,7 +153,7 @@
}
}
- private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures,
+ private int readCertsListXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures,
ArrayList<Signature> signatures, int count, boolean isPastSigs,
PackageParser.SigningDetails.Builder builder)
throws IOException, XmlPullParserException {
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
index 5a6fd09..6e6ab83 100644
--- a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
+++ b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
@@ -19,12 +19,13 @@
import android.content.ComponentName;
import android.content.IntentFilter;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
@@ -46,7 +47,8 @@
mIsSetByDpm = isSetByDpm;
}
- PersistentPreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
+ PersistentPreferredActivity(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
String shortComponent = parser.getAttributeValue(null, ATTR_NAME);
mComponent = ComponentName.unflattenFromString(shortComponent);
if (mComponent == null) {
@@ -86,7 +88,7 @@
}
}
- public void writeToXml(XmlSerializer serializer) throws IOException {
+ public void writeToXml(TypedXmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_NAME, mComponent.flattenToShortString());
serializer.attribute(null, ATTR_SET_BY_DPM, Boolean.toString(mIsSetByDpm));
serializer.startTag(null, ATTR_FILTER);
diff --git a/services/core/java/com/android/server/pm/PreferredActivity.java b/services/core/java/com/android/server/pm/PreferredActivity.java
index 8916926..4e1dcb2 100644
--- a/services/core/java/com/android/server/pm/PreferredActivity.java
+++ b/services/core/java/com/android/server/pm/PreferredActivity.java
@@ -16,15 +16,15 @@
package com.android.server.pm;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.content.ComponentName;
import android.content.IntentFilter;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -41,19 +41,19 @@
mPref = new PreferredComponent(this, match, set, activity, always);
}
- PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
+ PreferredActivity(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
mPref = new PreferredComponent(this, parser);
}
- public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
+ public void writeToXml(TypedXmlSerializer serializer, boolean full) throws IOException {
mPref.writeToXml(serializer, full);
serializer.startTag(null, "filter");
super.writeToXml(serializer);
serializer.endTag(null, "filter");
}
- public boolean onReadTag(String tagName, XmlPullParser parser) throws XmlPullParserException,
- IOException {
+ public boolean onReadTag(String tagName, TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
if (tagName.equals("filter")) {
if (DEBUG_FILTERS) {
Log.i(TAG, "Starting to parse filter...");
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index c312fc0..f743bbd 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -29,6 +29,8 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
@@ -57,7 +59,7 @@
private final String mSetupWizardPackageName;
public interface Callbacks {
- public boolean onReadTag(String tagName, XmlPullParser parser)
+ public boolean onReadTag(String tagName, TypedXmlPullParser parser)
throws XmlPullParserException, IOException;
}
@@ -97,7 +99,7 @@
}
}
- public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
+ public PreferredComponent(Callbacks callbacks, TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
mCallbacks = callbacks;
mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
@@ -182,7 +184,7 @@
return mParseError;
}
- public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
+ public void writeToXml(TypedXmlSerializer serializer, boolean full) throws IOException {
final int NS = mSetClasses != null ? mSetClasses.length : 0;
serializer.attribute(null, ATTR_NAME, mShortComponent);
if (full) {
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index 5aff6bb..4d74b08 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -22,12 +22,13 @@
import android.os.Bundle;
import android.os.UserManager;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
@@ -183,7 +184,7 @@
/**
* Serialize a given {@link RestrictionsSet} to XML.
*/
- public void writeRestrictions(@NonNull XmlSerializer serializer, @NonNull String outerTag)
+ public void writeRestrictions(@NonNull TypedXmlSerializer serializer, @NonNull String outerTag)
throws IOException {
serializer.startTag(null, outerTag);
for (int i = 0; i < mUserRestrictions.size(); i++) {
@@ -199,7 +200,7 @@
/**
* Read restrictions from XML.
*/
- public static RestrictionsSet readRestrictions(@NonNull XmlPullParser parser,
+ public static RestrictionsSet readRestrictions(@NonNull TypedXmlPullParser parser,
@NonNull String outerTag) throws IOException, XmlPullParserException {
RestrictionsSet restrictionsSet = new RestrictionsSet();
int userId = 0;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 71e7358..35fa7fc 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -74,6 +74,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -88,6 +89,8 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
@@ -96,13 +99,13 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalServices;
+import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -117,10 +120,7 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
@@ -1297,7 +1297,7 @@
*
* @see PreferredActivityBackupHelper
*/
- void readPreferredActivitiesLPw(XmlPullParser parser, int userId)
+ void readPreferredActivitiesLPw(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1330,7 +1330,7 @@
}
}
- private void readPersistentPreferredActivitiesLPw(XmlPullParser parser, int userId)
+ private void readPersistentPreferredActivitiesLPw(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1352,7 +1352,7 @@
}
}
- private void readCrossProfileIntentFiltersLPw(XmlPullParser parser, int userId)
+ private void readCrossProfileIntentFiltersLPw(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1374,8 +1374,8 @@
}
}
- private void readDomainVerificationLPw(XmlPullParser parser, PackageSettingBase packageSetting)
- throws XmlPullParserException, IOException {
+ private void readDomainVerificationLPw(TypedXmlPullParser parser,
+ PackageSettingBase packageSetting) throws XmlPullParserException, IOException {
IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
packageSetting.setIntentFilterVerificationInfo(ivi);
if (DEBUG_PARSER) {
@@ -1383,7 +1383,7 @@
}
}
- private void readRestoredIntentFilterVerifications(XmlPullParser parser)
+ private void readRestoredIntentFilterVerifications(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1407,7 +1407,7 @@
}
}
- void readDefaultAppsLPw(XmlPullParser parser, int userId)
+ void readDefaultAppsLPw(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1431,7 +1431,7 @@
}
}
- void readBlockUninstallPackagesLPw(XmlPullParser parser, int userId)
+ void readBlockUninstallPackagesLPw(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1521,8 +1521,7 @@
str = new FileInputStream(userPackagesStateFile);
if (DEBUG_MU) Log.i(TAG, "Reading " + userPackagesStateFile);
}
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
@@ -1752,7 +1751,7 @@
return packages.contains(packageName);
}
- private ArraySet<String> readComponentsLPr(XmlPullParser parser)
+ private ArraySet<String> readComponentsLPr(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
ArraySet<String> components = null;
int type;
@@ -1784,7 +1783,7 @@
* for recording packages.xml internally and for supporting backup/restore of the
* preferred activity configuration.
*/
- void writePreferredActivitiesLPr(XmlSerializer serializer, int userId, boolean full)
+ void writePreferredActivitiesLPr(TypedXmlSerializer serializer, int userId, boolean full)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, "preferred-activities");
PreferredIntentResolver pir = mPreferredActivities.get(userId);
@@ -1798,7 +1797,7 @@
serializer.endTag(null, "preferred-activities");
}
- void writePersistentPreferredActivitiesLPr(XmlSerializer serializer, int userId)
+ void writePersistentPreferredActivitiesLPr(TypedXmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES);
PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
@@ -1812,7 +1811,7 @@
serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES);
}
- void writeCrossProfileIntentFiltersLPr(XmlSerializer serializer, int userId)
+ void writeCrossProfileIntentFiltersLPr(TypedXmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId);
@@ -1826,7 +1825,7 @@
serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
}
- void writeDomainVerificationsLPr(XmlSerializer serializer,
+ void writeDomainVerificationsLPr(TypedXmlSerializer serializer,
IntentFilterVerificationInfo verificationInfo)
throws IllegalArgumentException, IllegalStateException, IOException {
if (verificationInfo != null && verificationInfo.getPackageName() != null) {
@@ -1841,7 +1840,7 @@
}
// Specifically for backup/restore
- void writeAllDomainVerificationsLPr(XmlSerializer serializer, int userId)
+ void writeAllDomainVerificationsLPr(TypedXmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION);
final int N = mPackages.size();
@@ -1856,7 +1855,7 @@
}
// Specifically for backup/restore
- void readAllDomainVerificationsLPr(XmlPullParser parser, int userId)
+ void readAllDomainVerificationsLPr(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
mRestoredIntentFilterVerifications.clear();
@@ -1896,7 +1895,7 @@
}
}
- void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
+ void writeDefaultAppsLPr(TypedXmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, TAG_DEFAULT_APPS);
String defaultBrowser = mDefaultBrowserApp.get(userId);
@@ -1908,7 +1907,7 @@
serializer.endTag(null, TAG_DEFAULT_APPS);
}
- void writeBlockUninstallPackagesLPr(XmlSerializer serializer, int userId)
+ void writeBlockUninstallPackagesLPr(TypedXmlSerializer serializer, int userId)
throws IOException {
ArraySet<String> packages = mBlockUninstallPackages.get(userId);
if (packages != null) {
@@ -1955,10 +1954,7 @@
try {
final FileOutputStream fstr = new FileOutputStream(userPackagesStateFile);
- final BufferedOutputStream str = new BufferedOutputStream(fstr);
-
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(str, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -2076,9 +2072,9 @@
serializer.endDocument();
- str.flush();
+ fstr.flush();
FileUtils.sync(fstr);
- str.close();
+ fstr.close();
// New settings successfully written, old ones are no longer
// needed.
@@ -2108,7 +2104,7 @@
}
}
- void readInstallPermissionsLPr(XmlPullParser parser,
+ void readInstallPermissionsLPr(TypedXmlPullParser parser,
LegacyPermissionState permissionsState) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
@@ -2138,7 +2134,7 @@
}
}
- void writePermissionsLPr(XmlSerializer serializer, Collection<PermissionState> permissionStates)
+ void writePermissionsLPr(TypedXmlSerializer serializer, Collection<PermissionState> permissionStates)
throws IOException {
if (permissionStates.isEmpty()) {
return;
@@ -2157,7 +2153,7 @@
serializer.endTag(null, TAG_PERMISSIONS);
}
- void readUsesStaticLibLPw(XmlPullParser parser, PackageSetting outPs)
+ void readUsesStaticLibLPw(TypedXmlPullParser parser, PackageSetting outPs)
throws IOException, XmlPullParserException {
String libName = parser.getAttributeValue(null, ATTR_NAME);
String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION);
@@ -2179,7 +2175,7 @@
XmlUtils.skipCurrentTag(parser);
}
- void writeUsesStaticLibLPw(XmlSerializer serializer, String[] usesStaticLibraries,
+ void writeUsesStaticLibLPw(TypedXmlSerializer serializer, String[] usesStaticLibraries,
long[] usesStaticLibraryVersions) throws IOException {
if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions)
|| usesStaticLibraries.length != usesStaticLibraryVersions.length) {
@@ -2236,8 +2232,7 @@
}
str = new FileInputStream(mStoppedPackagesFilename);
}
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
@@ -2334,12 +2329,8 @@
mPastSignatures.clear();
try {
- FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
- BufferedOutputStream str = new BufferedOutputStream(fstr);
-
- //XmlSerializer serializer = XmlUtils.serializerInstance();
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(str, StandardCharsets.UTF_8.name());
+ final FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -2429,9 +2420,9 @@
serializer.endDocument();
- str.flush();
+ fstr.flush();
FileUtils.sync(fstr);
- str.close();
+ fstr.close();
// New settings successfully written, old ones are no longer
// needed.
@@ -2685,7 +2676,7 @@
}
}
- void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
+ void writeDisabledSysPackageLPr(TypedXmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "updated-package");
serializer.attribute(null, ATTR_NAME, pkg.name);
@@ -2727,7 +2718,7 @@
serializer.endTag(null, "updated-package");
}
- void writePackageLPr(XmlSerializer serializer, final PackageSetting pkg)
+ void writePackageLPr(TypedXmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "package");
serializer.attribute(null, ATTR_NAME, pkg.name);
@@ -2823,7 +2814,7 @@
serializer.endTag(null, "package");
}
- void writeSigningKeySetLPr(XmlSerializer serializer,
+ void writeSigningKeySetLPr(TypedXmlSerializer serializer,
PackageKeySetData data) throws IOException {
serializer.startTag(null, "proper-signing-keyset");
serializer.attribute(null, "identifier",
@@ -2831,7 +2822,7 @@
serializer.endTag(null, "proper-signing-keyset");
}
- void writeUpgradeKeySetsLPr(XmlSerializer serializer,
+ void writeUpgradeKeySetsLPr(TypedXmlSerializer serializer,
PackageKeySetData data) throws IOException {
if (data.isUsingUpgradeKeySets()) {
for (long id : data.getUpgradeKeySets()) {
@@ -2842,7 +2833,7 @@
}
}
- void writeKeySetAliasesLPr(XmlSerializer serializer,
+ void writeKeySetAliasesLPr(TypedXmlSerializer serializer,
PackageKeySetData data) throws IOException {
for (Map.Entry<String, Long> e: data.getAliases().entrySet()) {
serializer.startTag(null, "defined-keyset");
@@ -2892,8 +2883,7 @@
}
str = new FileInputStream(mSettingsFilename);
}
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -3147,9 +3137,8 @@
Log.d(TAG, "Reading default preferred " + f);
}
- try (InputStream str = new BufferedInputStream(new FileInputStream(f))) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
+ try (InputStream str = new FileInputStream(f)) {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -3420,7 +3409,7 @@
}
}
- private void readDefaultPreferredActivitiesLPw(XmlPullParser parser, int userId)
+ private void readDefaultPreferredActivitiesLPw(TypedXmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
final PackageManagerInternal pmInternal =
LocalServices.getService(PackageManagerInternal.class);
@@ -3452,8 +3441,8 @@
}
}
- private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException,
- IOException {
+ private void readDisabledSysPackageLPw(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
String name = parser.getAttributeValue(null, ATTR_NAME);
String realName = parser.getAttributeValue(null, "realName");
String codePathStr = parser.getAttributeValue(null, "codePath");
@@ -3551,7 +3540,8 @@
private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
- private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
+ private void readPackageLPw(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
String name = null;
String realName = null;
String idStr = null;
@@ -3891,7 +3881,7 @@
}
}
- private Map<String, ArraySet<String>> readMimeGroupLPw(XmlPullParser parser,
+ private Map<String, ArraySet<String>> readMimeGroupLPw(TypedXmlPullParser parser,
Map<String, ArraySet<String>> mimeGroups) throws XmlPullParserException, IOException {
String groupName = parser.getAttributeValue(null, ATTR_NAME);
if (groupName == null) {
@@ -3932,7 +3922,7 @@
return mimeGroups;
}
- private void writeMimeGroupLPr(XmlSerializer serializer,
+ private void writeMimeGroupLPr(TypedXmlSerializer serializer,
Map<String, ArraySet<String>> mimeGroups) throws IOException {
if (mimeGroups == null) {
return;
@@ -3952,8 +3942,8 @@
}
}
- private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser,
- int userId) throws IOException, XmlPullParserException {
+ private void readDisabledComponentsLPw(PackageSettingBase packageSetting,
+ TypedXmlPullParser parser, int userId) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3980,8 +3970,8 @@
}
}
- private void readEnabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser,
- int userId) throws IOException, XmlPullParserException {
+ private void readEnabledComponentsLPw(PackageSettingBase packageSetting,
+ TypedXmlPullParser parser, int userId) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4008,7 +3998,8 @@
}
}
- private void readSharedUserLPw(XmlPullParser parser) throws XmlPullParserException,IOException {
+ private void readSharedUserLPw(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
String name = null;
String idStr = null;
int pkgFlags = 0;
@@ -5547,8 +5538,7 @@
}
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, null);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
parseRuntimePermissionsLPr(parser, userId);
} catch (XmlPullParserException | IOException e) {
@@ -5562,7 +5552,7 @@
// Private internals
@GuardedBy("Settings.this.mLock")
- private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
+ private void parseRuntimePermissionsLPr(TypedXmlPullParser parser, int userId)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
@@ -5608,7 +5598,7 @@
}
}
- private void parsePermissionsLPr(XmlPullParser parser,
+ private void parsePermissionsLPr(TypedXmlPullParser parser,
LegacyPermissionState permissionsState, int userId)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 24082b8..3b91d6a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -94,6 +94,8 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -102,7 +104,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -121,7 +122,6 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -131,7 +131,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -2548,8 +2547,7 @@
AtomicFile userListFile = new AtomicFile(mUserListFile);
try {
fis = userListFile.openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
@@ -2874,8 +2872,7 @@
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userData.info.id + XML_SUFFIX));
try {
fos = userFile.startWrite();
- final BufferedOutputStream bos = new BufferedOutputStream(fos);
- writeUserLP(userData, bos);
+ writeUserLP(userData, fos);
userFile.finishWrite(fos);
} catch (Exception ioe) {
Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
@@ -2893,9 +2890,7 @@
@VisibleForTesting
void writeUserLP(UserData userData, OutputStream os)
throws IOException, XmlPullParserException {
- // XmlSerializer serializer = XmlUtils.serializerInstance();
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(os, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(os);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -3001,11 +2996,7 @@
AtomicFile userListFile = new AtomicFile(mUserListFile);
try {
fos = userListFile.startWrite();
- final BufferedOutputStream bos = new BufferedOutputStream(fos);
-
- // XmlSerializer serializer = XmlUtils.serializerInstance();
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(bos, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -3092,8 +3083,7 @@
RestrictionsSet localRestrictions = null;
Bundle globalRestrictions = null;
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(is, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(is);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
@@ -3231,7 +3221,7 @@
return userData;
}
- private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
+ private int readIntAttribute(TypedXmlPullParser parser, String attr, int defaultValue) {
String valueString = parser.getAttributeValue(null, attr);
if (valueString == null) return defaultValue;
try {
@@ -3241,7 +3231,7 @@
}
}
- private long readLongAttribute(XmlPullParser parser, String attr, long defaultValue) {
+ private long readLongAttribute(TypedXmlPullParser parser, String attr, long defaultValue) {
String valueString = parser.getAttributeValue(null, attr);
if (valueString == null) return defaultValue;
try {
@@ -4259,8 +4249,7 @@
FileInputStream fis = null;
try {
fis = restrictionsFile.openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
XmlUtils.nextElement(parser);
if (parser.getEventType() != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read restrictions file "
@@ -4279,7 +4268,7 @@
}
private static void readEntry(Bundle restrictions, ArrayList<String> values,
- XmlPullParser parser) throws XmlPullParserException, IOException {
+ TypedXmlPullParser parser) throws XmlPullParserException, IOException {
int type = parser.getEventType();
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
String key = parser.getAttributeValue(null, ATTR_KEY);
@@ -4322,7 +4311,7 @@
}
}
- private static Bundle readBundleEntry(XmlPullParser parser, ArrayList<String> values)
+ private static Bundle readBundleEntry(TypedXmlPullParser parser, ArrayList<String> values)
throws IOException, XmlPullParserException {
Bundle childBundle = new Bundle();
final int outerDepth = parser.getDepth();
@@ -4347,10 +4336,7 @@
FileOutputStream fos = null;
try {
fos = restrictionsFile.startWrite();
- final BufferedOutputStream bos = new BufferedOutputStream(fos);
-
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(bos, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -4366,7 +4352,7 @@
}
}
- private static void writeBundle(Bundle restrictions, XmlSerializer serializer)
+ private static void writeBundle(Bundle restrictions, TypedXmlSerializer serializer)
throws IOException {
for (String key : restrictions.keySet()) {
Object value = restrictions.get(key);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 87a883c..145dd24 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -41,8 +41,11 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import com.google.android.collect.Sets;
@@ -327,6 +330,11 @@
public static void writeRestrictions(@NonNull XmlSerializer serializer,
@Nullable Bundle restrictions, @NonNull String tag) throws IOException {
+ writeRestrictions(XmlUtils.makeTyped(serializer), restrictions, tag);
+ }
+
+ public static void writeRestrictions(@NonNull TypedXmlSerializer serializer,
+ @Nullable Bundle restrictions, @NonNull String tag) throws IOException {
if (restrictions == null) {
return;
}
@@ -348,6 +356,10 @@
}
public static void readRestrictions(XmlPullParser parser, Bundle restrictions) {
+ readRestrictions(XmlUtils.makeTyped(parser), restrictions);
+ }
+
+ public static void readRestrictions(TypedXmlPullParser parser, Bundle restrictions) {
restrictions.clear();
for (String key : USER_RESTRICTIONS) {
final String value = parser.getAttributeValue(null, key);
@@ -358,6 +370,10 @@
}
public static Bundle readRestrictions(XmlPullParser parser) {
+ return readRestrictions(XmlUtils.makeTyped(parser));
+ }
+
+ public static Bundle readRestrictions(TypedXmlPullParser parser) {
final Bundle result = new Bundle();
readRestrictions(parser, result);
return result;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 2d2e72a..9dde7df 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -275,8 +275,8 @@
return null;
}
- ActivityInfo info =
- PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(a, applicationInfo);
+ final ActivityInfo info = PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(
+ a, flags, applicationInfo);
assignSharedFieldsForComponentInfo(info, a, pkgSetting, userId);
return info;
}
@@ -310,8 +310,8 @@
return null;
}
- ServiceInfo info =
- PackageInfoWithoutStateUtils.generateServiceInfoUnchecked(s, applicationInfo);
+ final ServiceInfo info = PackageInfoWithoutStateUtils.generateServiceInfoUnchecked(
+ s, flags, applicationInfo);
assignSharedFieldsForComponentInfo(info, s, pkgSetting, userId);
return info;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 7a37bdd..8940256 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -60,6 +60,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
import android.util.Xml;
import com.android.internal.util.ArrayUtils;
@@ -1410,7 +1411,7 @@
try (
InputStream str = new BufferedInputStream(new FileInputStream(file))
) {
- XmlPullParser parser = Xml.newPullParser();
+ TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(str, null);
parse(pm, parser, grantExceptions);
} catch (XmlPullParserException | IOException e) {
@@ -1421,7 +1422,7 @@
return grantExceptions;
}
- private void parse(PackageManagerWrapper pm, XmlPullParser parser,
+ private void parse(PackageManagerWrapper pm, TypedXmlPullParser parser,
Map<String, List<DefaultPermissionGrant>> outGrantExceptions)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
@@ -1439,7 +1440,7 @@
}
}
- private void parseExceptions(PackageManagerWrapper pm, XmlPullParser parser,
+ private void parseExceptions(PackageManagerWrapper pm, TypedXmlPullParser parser,
Map<String, List<DefaultPermissionGrant>> outGrantExceptions)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
@@ -1488,7 +1489,7 @@
}
}
- private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
+ private void parsePermission(TypedXmlPullParser parser, List<DefaultPermissionGrant>
outPackageExceptions) throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java
index a19a05a..48243a4 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermission.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java
@@ -21,15 +21,14 @@
import android.annotation.Nullable;
import android.content.pm.PermissionInfo;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
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;
@@ -133,7 +132,7 @@
* @hide
*/
public static boolean read(@NonNull Map<String, LegacyPermission> out,
- @NonNull XmlPullParser parser) {
+ @NonNull TypedXmlPullParser parser) {
final String tagName = parser.getName();
if (!tagName.equals(TAG_ITEM)) {
return false;
@@ -166,7 +165,7 @@
return true;
}
- private static int readInt(@NonNull XmlPullParser parser, @Nullable String namespace,
+ private static int readInt(@NonNull TypedXmlPullParser parser, @Nullable String namespace,
@NonNull String name, int defaultValue) {
final String value = parser.getAttributeValue(namespace, name);
if (value == null) {
@@ -186,7 +185,7 @@
/**
* @hide
*/
- public void write(@NonNull XmlSerializer serializer) throws IOException {
+ public void write(@NonNull TypedXmlSerializer serializer) throws IOException {
if (mPermissionInfo.packageName == null) {
return;
}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java
index cc0b798..f63600a 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java
@@ -21,6 +21,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.XmlUtils;
@@ -99,14 +101,14 @@
}
}
- public void readPermissions(@NonNull XmlPullParser parser) throws IOException,
+ public void readPermissions(@NonNull TypedXmlPullParser parser) throws IOException,
XmlPullParserException {
synchronized (mLock) {
readPermissions(mPermissions, parser);
}
}
- public void readPermissionTrees(@NonNull XmlPullParser parser) throws IOException,
+ public void readPermissionTrees(@NonNull TypedXmlPullParser parser) throws IOException,
XmlPullParserException {
synchronized (mLock) {
readPermissions(mPermissionTrees, parser);
@@ -114,7 +116,7 @@
}
public static void readPermissions(@NonNull ArrayMap<String, LegacyPermission> out,
- @NonNull XmlPullParser parser) throws IOException, XmlPullParserException {
+ @NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -132,7 +134,7 @@
}
}
- public void writePermissions(@NonNull XmlSerializer serializer) throws IOException {
+ public void writePermissions(@NonNull TypedXmlSerializer serializer) throws IOException {
synchronized (mLock) {
for (LegacyPermission bp : mPermissions.values()) {
bp.write(serializer);
@@ -140,7 +142,7 @@
}
}
- public void writePermissionTrees(@NonNull XmlSerializer serializer) throws IOException {
+ public void writePermissionTrees(@NonNull TypedXmlSerializer serializer) throws IOException {
synchronized (mLock) {
for (LegacyPermission bp : mPermissionTrees.values()) {
bp.write(serializer);
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
index c08b610..1387617 100644
--- a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
+++ b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
@@ -23,6 +23,8 @@
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -316,8 +318,7 @@
outs = file.startWrite();
// Write to XML
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outs, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(outs);
out.startDocument(null, true);
out.startTag(null, TAG_DEFAULT_ROOT);
@@ -344,8 +345,7 @@
}
Map<String, String> read = null;
try (FileInputStream in = file.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(in);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index aafadf9..d2614e4 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -46,6 +46,8 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseLongArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -280,9 +282,8 @@
private void writeXmlToFile(List<CacheQuotaHint> processedRequests) {
FileOutputStream fileStream = null;
try {
- XmlSerializer out = new FastXmlSerializer();
fileStream = mPreviousValuesFile.startWrite();
- out.setOutput(fileStream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fileStream);
saveToXml(out, processedRequests, 0);
mPreviousValuesFile.finishWrite(fileStream);
} catch (Exception e) {
@@ -342,8 +343,7 @@
protected static Pair<Long, List<CacheQuotaHint>> readFromXml(InputStream inputStream)
throws XmlPullParserException, IOException {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index 04f0871..7652c43 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -25,6 +25,8 @@
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import java.io.File;
@@ -265,8 +267,7 @@
private static XmlPullParser parseToPackageStatusTag(FileInputStream fis)
throws ParseException {
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int type;
while ((type = parser.next()) != END_DOCUMENT) {
final String tag = parser.getName();
@@ -315,8 +316,7 @@
FileOutputStream fos = null;
try {
fos = mPackageStatusFile.startWrite();
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
serializer.startDocument(null /* encoding */, true /* standalone */);
final String namespace = null;
serializer.startTag(namespace, TAG_PACKAGE_STATUS);
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index 8f2194c..355c385 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -26,6 +26,7 @@
import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -167,8 +168,7 @@
XmlPullParser parser;
try {
- parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
+ parser = Xml.resolvePullParser(is);
loadFromXml(parser);
} catch (IOException | XmlPullParserException ex) {
Slog.w(TAG, "Failed to load tv input manager persistent store data.", ex);
@@ -200,8 +200,7 @@
os = mAtomicFile.startWrite();
boolean success = false;
try {
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(os);
saveToXml(serializer);
serializer.flush();
success = true;
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index f6acc64..bbb5374 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -75,6 +75,8 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -657,8 +659,7 @@
FileInputStream fis = null;
try {
fis = mGrantFile.openRead();
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser in = Xml.resolvePullParser(fis);
int type;
while ((type = in.next()) != END_DOCUMENT) {
@@ -1313,8 +1314,7 @@
try {
fos = mGrantFile.startWrite(startTime);
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_URI_GRANTS);
for (UriPermission.Snapshot perm : persist) {
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
index c160647..12c6a7a 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
@@ -21,14 +21,14 @@
import com.android.internal.util.Preconditions;
-import java.util.ConcurrentModificationException;
import java.util.ListIterator;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
/**
* An in-memory event log to support historical event information.
*/
-public abstract class LocalEventLog implements Iterable<String> {
+public abstract class LocalEventLog {
private interface Log {
// true if this is a filler element that should not be queried
@@ -93,7 +93,6 @@
private final Log[] mLog;
private int mLogSize;
private int mLogEndIndex;
- private int mModificationCount;
// invalid if log is empty
private long mStartRealtimeMs;
@@ -103,7 +102,6 @@
mLog = new Log[size];
mLogSize = 0;
mLogEndIndex = 0;
- mModificationCount = 0;
mStartRealtimeMs = -1;
mLastLogRealtimeMs = -1;
@@ -128,7 +126,7 @@
* into {@link #createLogEvent(long, int, Object...)} in addition to a time delta, and should be
* used to construct an appropriate {@link LogEvent} object.
*/
- public void addLogEvent(int event, Object... args) {
+ public synchronized void addLogEvent(int event, Object... args) {
long timeMs = SystemClock.elapsedRealtime();
// calculate delta
@@ -175,28 +173,28 @@
mLog[mLogEndIndex] = event;
mLogEndIndex = incrementIndex(mLogEndIndex);
mLastLogRealtimeMs = mLastLogRealtimeMs + event.getTimeDeltaMs();
-
- mModificationCount++;
}
/** Clears the log of all entries. */
- public void clear() {
+ public synchronized void clear() {
mLogEndIndex = 0;
mLogSize = 0;
- mModificationCount++;
mStartRealtimeMs = -1;
mLastLogRealtimeMs = -1;
}
// checks if the log is empty (if empty, times are invalid)
- private boolean isEmpty() {
+ private synchronized boolean isEmpty() {
return mLogSize == 0;
}
- @Override
- public ListIterator<String> iterator() {
- return new LogIterator();
+ /** Iterates over the event log, passing each log string to the given consumer. */
+ public synchronized void iterate(Consumer<String> consumer) {
+ LogIterator it = new LogIterator();
+ while (it.hasNext()) {
+ consumer.accept(it.next());
+ }
}
// returns the index of the first element
@@ -222,8 +220,6 @@
private class LogIterator implements ListIterator<String> {
- private final int mModificationGuard;
-
private final long mSystemTimeDeltaMs;
private long mCurrentRealtimeMs;
@@ -231,7 +227,6 @@
private int mCount;
LogIterator() {
- mModificationGuard = mModificationCount;
mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
mCurrentRealtimeMs = mStartRealtimeMs;
mIndex = startIndex();
@@ -251,8 +246,6 @@
@Override
// return then increment
public String next() {
- checkModifications();
-
if (!hasNext()) {
throw new NoSuchElementException();
}
@@ -276,8 +269,6 @@
@Override
// decrement then return
public String previous() {
- checkModifications();
-
Log log;
long currentDeltaMs;
long realtimeMs;
@@ -303,12 +294,6 @@
return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
}
- private void checkModifications() {
- if (mModificationGuard != mModificationCount) {
- throw new ConcurrentModificationException();
- }
- }
-
@Override
public int nextIndex() {
throw new UnsupportedOperationException();
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index ad2b3354..06a1f59 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -152,8 +152,8 @@
/**
* Return {@code true} if the device should vibrate for ringtones.
*
- * <p>This checks the current {@link AudioManager#getRingerMode()} against user settings for
- * vibrations while ringing.
+ * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
+ * for vibrations while ringing.
*/
public boolean shouldVibrateForRingtone() {
int ringerMode = mAudioManager.getRingerModeInternal();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8b0963b..329589e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -90,6 +90,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
@@ -2910,10 +2912,9 @@
FileOutputStream fstream = null;
BufferedOutputStream stream = null;
try {
- XmlSerializer out = new FastXmlSerializer();
fstream = new FileOutputStream(journal.chooseForWrite(), false);
stream = new BufferedOutputStream(fstream);
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
WallpaperData wallpaper;
@@ -3110,8 +3111,7 @@
final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
do {
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 21b6809..d40dea2 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -27,6 +27,8 @@
import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -477,8 +479,7 @@
try {
fos = mConfigFile.startWrite();
- final XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "packages");
@@ -519,8 +520,7 @@
try {
fis = mConfigFile.openRead();
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 7e55f0a..120cf7d 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -35,6 +35,8 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.protolog.common.ProtoLog;
@@ -93,8 +95,7 @@
FileInputStream fis = null;
try {
fis = mFile.openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(fis);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
eventType != XmlPullParser.END_DOCUMENT) {
@@ -362,8 +363,7 @@
try {
fos = mFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "compat-packages");
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index a7f7c48..83da136e 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -28,6 +28,8 @@
import android.os.Environment;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
@@ -280,8 +282,7 @@
FileData fileData = new FileData();
boolean success = false;
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
@@ -419,8 +420,7 @@
boolean success = false;
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "display-settings");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ac9c289..90c3f9e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5888,7 +5888,21 @@
try {
// Protect against recursion.
mInResumeTopActivity = true;
- result = resumeTopActivityInnerLocked(prev, options);
+
+ // TODO(b/172885410): Allow the top activities of all visible leaf tasks to be resumed
+ if (mCreatedByOrganizer && !isLeafTask()
+ && getConfiguration().windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final Task child = (Task) getChildAt(i);
+ if (!child.shouldBeVisible(null /* starting */)) {
+ break;
+ }
+ result |= child.resumeTopActivityUncheckedLocked(prev, options);
+ }
+ } else {
+ result = resumeTopActivityInnerLocked(prev, options);
+ }
// When resuming the top activity, it may be necessary to pause the top activity (for
// example, returning to the lock screen. We suppress the normal pause logic in
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f11cd93..499fbf6 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -434,8 +434,7 @@
if (hop.isReparent()) {
final boolean isNonOrganizedRootableTask =
- (task.isRootTask() && !task.mCreatedByOrganizer)
- || task.getParent().asTask().mCreatedByOrganizer;
+ task.isRootTask() || task.getParent().asTask().mCreatedByOrganizer;
if (isNonOrganizedRootableTask) {
WindowContainer newParent = hop.getNewParent() == null
? dc.getDefaultTaskDisplayArea()
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 130cfd5..bdb7f79 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -25,6 +25,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -154,8 +156,7 @@
FileOutputStream stream = null;
try {
stream = new FileOutputStream(file.chooseForWrite(), false);
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "policies");
@@ -372,8 +373,7 @@
boolean needsRewrite = false;
try {
stream = new FileInputStream(file);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9d634bc..ad3a52f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -255,6 +255,8 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -14204,8 +14206,7 @@
return null;
}
try (FileInputStream stream = new FileInputStream(bundleFile)) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, null);
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
parser.next();
return PersistableBundle.restoreFromXml(parser);
} catch (IOException | XmlPullParserException | IllegalArgumentException e) {
@@ -14358,8 +14359,7 @@
FileOutputStream stream = null;
try {
stream = atomicFile.startWrite();
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(stream);
serializer.startDocument(null, true);
serializer.startTag(null, TAG_TRANSFER_OWNERSHIP_BUNDLE);
bundle.saveToXml(serializer);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index cced359..9e98fc5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -39,6 +39,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -735,8 +737,7 @@
FileOutputStream outputStream = null;
try {
outputStream = f.startWrite();
- final XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer out = Xml.resolveSerializer(outputStream);
// Root tag
out.startDocument(null, true);
@@ -776,8 +777,7 @@
InputStream input = null;
try {
input = f.openRead();
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(input, StandardCharsets.UTF_8.name());
+ final TypedXmlPullParser parser = Xml.resolvePullParser(input);
int type;
int depth = 0;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
index 4b66bea..58ece07 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
@@ -24,6 +24,8 @@
import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -84,8 +86,7 @@
FileOutputStream stream = null;
try {
stream = atomicFile.startWrite();
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(stream);
serializer.startDocument(null, true);
insertSimpleTag(serializer, TAG_USER_ID, Integer.toString(params.userId));
insertSimpleTag(serializer,
@@ -122,8 +123,7 @@
Slog.d(TAG, "Loading TransferOwnershipMetadataManager from "
+ transferOwnershipMetadataFile);
try (FileInputStream stream = new FileInputStream(transferOwnershipMetadataFile)) {
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, null);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(stream);
return parseMetadataFile(parser);
} catch (IOException | XmlPullParserException | IllegalArgumentException e) {
Slog.e(TAG, "Caught exception while trying to load the "
diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp
new file mode 100644
index 0000000..6dd059f
--- /dev/null
+++ b/services/tests/inprocesstests/Android.bp
@@ -0,0 +1,12 @@
+android_test {
+ name: "FrameworksInProcessTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "services.core",
+ "truth-prebuilt",
+ "platform-test-annotations",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/services/tests/inprocesstests/AndroidManifest.xml b/services/tests/inprocesstests/AndroidManifest.xml
new file mode 100644
index 0000000..efb4a53
--- /dev/null
+++ b/services/tests/inprocesstests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.frameworks.inprocesstests" >
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android"
+ android:label="Frameworks In-Process Tests"/>
+
+</manifest>
diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml
new file mode 100644
index 0000000..89abe3c
--- /dev/null
+++ b/services/tests/inprocesstests/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs frameworks in-process tests">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="FrameworksInProcessTests.apk"/>
+ </target_preparer>
+
+ <!-- Restart to clear test code from system server -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="teardown-command" value="am restart" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.frameworks.inprocesstests"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="restart" value="false" />
+ </test>
+</configuration>
diff --git a/services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java b/services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java
new file mode 100644
index 0000000..47fc73f
--- /dev/null
+++ b/services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.frameworks.inprocesstests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Process;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+
+public class InstrumentSystemServerTest {
+
+ private static final String TAG = "InstrumentSystemServerTest";
+
+ @Test
+ public void testCodeIsRunningInSystemServer() throws Exception {
+ assertThat(InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName())
+ .isEqualTo("android");
+ assertThat(Process.myUid()).isEqualTo(Process.SYSTEM_UID);
+ assertThat(readCmdLine()).isEqualTo("system_server");
+ }
+
+ private static String readCmdLine() throws Exception {
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+ return in.readLine().trim();
+ } finally {
+ in.close();
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 5ddb571..c1f83cb 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -806,7 +806,7 @@
private void setRingerMode(int ringerMode) {
AudioManager audioManager = mContextSpy.getSystemService(AudioManager.class);
audioManager.setRingerModeInternal(ringerMode);
- assertEquals(ringerMode, audioManager.getRingerMode());
+ assertEquals(ringerMode, audioManager.getRingerModeInternal());
}
private void setUserSetting(String settingName, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index e8c9697..6b000f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -24,12 +24,16 @@
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.content.Context;
@@ -39,6 +43,9 @@
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorProperties;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -79,7 +86,8 @@
private IBinder mToken;
// Assume all tests can be done with the same set of sensors for now.
- private List<BiometricSensor> mSensors;
+ @NonNull private List<BiometricSensor> mSensors;
+ @NonNull private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProps;
@Before
public void setUp() throws Exception {
@@ -88,11 +96,12 @@
mRandom = new Random();
mToken = new Binder();
mSensors = new ArrayList<>();
+ mFingerprintSensorProps = new ArrayList<>();
}
@Test
public void testNewAuthSession_eligibleSensorsSetToStateUnknown() throws RemoteException {
- setupFingerprint(0 /* id */);
+ setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR);
setupFace(1 /* id */, false /* confirmationAlwaysRequired */);
final AuthSession session = createAuthSession(mSensors,
@@ -110,10 +119,9 @@
}
@Test
- public void testStartNewAuthSession()
- throws RemoteException {
+ public void testStartNewAuthSession() throws RemoteException {
setupFace(0 /* id */, false /* confirmationAlwaysRequired */);
- setupFingerprint(1 /* id */);
+ setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_REAR);
final boolean requireConfirmation = true;
final long operationId = 123;
@@ -175,6 +183,60 @@
}
}
+ @Test
+ public void testUdfpsAuth_sensorStartsAfterDialogAnimationCompletes() throws RemoteException {
+ // For UDFPS-only setups, ensure that the sensor does not start auth until after the
+ // BiometricPrompt UI is finished animating. Otherwise, the UDFPS affordance will be
+ // shown before the BiometricPrompt is shown.
+ setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
+
+ final long operationId = 123;
+ final int userId = 10;
+ final int callingUid = 100;
+ final int callingPid = 1000;
+ final int callingUserId = 10000;
+
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ operationId,
+ userId,
+ callingUid,
+ callingPid,
+ callingUserId);
+ assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
+
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+ assertEquals(0, sensor.getCookie());
+ }
+
+ session.goToInitialState();
+
+ final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie();
+ session.onCookieReceived(cookie1);
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ if (cookie1 == sensor.getCookie()) {
+ assertEquals(BiometricSensor.STATE_COOKIE_RETURNED, sensor.getSensorState());
+ } else {
+ assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState());
+ }
+ }
+ assertTrue(session.allCookiesReceived());
+
+ // UDFPS does not start even if all cookies are received
+ assertEquals(AuthSession.STATE_AUTH_STARTED, session.getState());
+ verify(mStatusBarService).showAuthenticationDialog(any(), any(), any(),
+ anyBoolean(), anyBoolean(), anyInt(), any(), anyLong());
+
+ // Notify AuthSession that the UI is shown. Then, UDFPS sensor should be started.
+ session.onDialogAnimatedIn();
+ assertEquals(AuthSession.STATE_AUTH_STARTED_UI_SHOWING, session.getState());
+ assertEquals(BiometricSensor.STATE_AUTHENTICATING,
+ session.mPreAuthInfo.eligibleSensors.get(0).getSensorState());
+
+ }
+
private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId,
PromptInfo promptInfo, boolean checkDevicePolicyManager) throws RemoteException {
return PreAuthInfo.create(mTrustManager,
@@ -197,11 +259,10 @@
final PreAuthInfo preAuthInfo = createPreAuthInfo(sensors, userId, promptInfo,
checkDevicePolicyManager);
-
return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore,
mRandom, mClientDeathReceiver, preAuthInfo, mToken, operationId, userId,
mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, callingUid,
- callingPid, callingUserId, false /* debugEnabled */);
+ callingPid, callingUserId, false /* debugEnabled */, mFingerprintSensorProps);
}
private PromptInfo createPromptInfo(@Authenticators.Types int authenticators) {
@@ -210,8 +271,8 @@
return promptInfo;
}
-
- private void setupFingerprint(int id) throws RemoteException {
+ private void setupFingerprint(int id, @FingerprintSensorProperties.SensorType int type)
+ throws RemoteException {
IBiometricAuthenticator fingerprintAuthenticator = mock(IBiometricAuthenticator.class);
when(fingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
when(fingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
@@ -229,6 +290,12 @@
return false; // fingerprint does not support confirmation
}
});
+
+ mFingerprintSensorProps.add(new FingerprintSensorPropertiesInternal(id,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ type,
+ false /* resetLockoutRequiresHardwareAuthToken */));
}
private void setupFace(int id, boolean confirmationAlwaysRequired) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index c890c52..24e7d7d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -20,20 +20,27 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
+import android.os.Binder;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -44,8 +51,10 @@
public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
+ private static final int TEST_SENSOR_ID = 1;
private BiometricScheduler mScheduler;
+ private IBinder mToken;
@Mock
private Context mContext;
@@ -55,6 +64,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mToken = new Binder();
mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */,
mBiometricService);
}
@@ -63,8 +73,8 @@
public void testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash() {
final ClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
- final ClientMonitor<Object> client1 = new TestClientMonitor(mContext, nonNullDaemon);
- final ClientMonitor<Object> client2 = new TestClientMonitor(mContext, nonNullDaemon);
+ final ClientMonitor<Object> client1 = new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ final ClientMonitor<Object> client2 = new TestClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
@@ -80,8 +90,8 @@
final ClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
final ClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
- final TestClientMonitor client1 = new TestClientMonitor(mContext, lazyDaemon1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, lazyDaemon2);
+ final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1);
+ final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
final ClientMonitor.Callback callback1 = mock(ClientMonitor.Callback.class);
final ClientMonitor.Callback callback2 = mock(ClientMonitor.Callback.class);
@@ -110,16 +120,18 @@
}
@Test
- public void testRemovesOnlyBiometricPromptOperation_whenNullHal() {
+ public void testRemovesOnlyBiometricPromptOperation_whenNullHal() throws Exception {
// Second non-BiometricPrompt client has a valid daemon
final Object daemon2 = mock(Object.class);
final ClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
final ClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
- final TestClientMonitor client1 =
- new TestBiometricPromptClientMonitor(mContext, lazyDaemon1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, lazyDaemon2);
+ final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
+
+ final BiometricPromptClientMonitor client1 =
+ new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, listener1);
+ final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
final ClientMonitor.Callback callback1 = mock(ClientMonitor.Callback.class);
final ClientMonitor.Callback callback2 = mock(ClientMonitor.Callback.class);
@@ -139,8 +151,10 @@
// Simulate that the BiometricPrompt client's sensor is ready
mScheduler.startPreparedClient(client1.getCookie());
- assertTrue(client1.wasUnableToStart());
- verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
+ // Client 1 cleans up properly
+ verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(),
+ eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0));
+ verify(callback1).onClientFinished(eq(client1), eq(true) /* success */);
verify(callback1, never()).onClientStarted(any());
// Client 2 was able to start
@@ -149,10 +163,45 @@
verify(callback2).onClientStarted(eq(client2));
}
- private static class TestBiometricPromptClientMonitor extends TestClientMonitor {
- public TestBiometricPromptClientMonitor(@NonNull Context context,
- @NonNull LazyDaemon<Object> lazyDaemon) {
- super(context, lazyDaemon, 1 /* cookie */);
+ @Test
+ public void testCancelNotInvoked_whenOperationWaitingForCookie() {
+ final ClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
+ final BiometricPromptClientMonitor client1 = new BiometricPromptClientMonitor(mContext,
+ mToken, lazyDaemon1, mock(ClientMonitorCallbackConverter.class));
+ final ClientMonitor.Callback callback1 = mock(ClientMonitor.Callback.class);
+
+ // Schedule a BiometricPrompt authentication request
+ mScheduler.scheduleClientMonitor(client1, callback1);
+
+ assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.state);
+ assertEquals(client1, mScheduler.mCurrentOperation.clientMonitor);
+ assertEquals(0, mScheduler.mPendingOperations.size());
+
+ // Request it to be canceled. The operation can be canceled immediately, and the scheduler
+ // should go back to idle, since in this case the framework has not even requested the HAL
+ // to authenticate yet.
+ mScheduler.cancelAuthentication(mToken);
+ assertNull(mScheduler.mCurrentOperation);
+ }
+
+ private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
+
+ public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ @NonNull LazyDaemon<Object> lazyDaemon, ClientMonitorCallbackConverter listener) {
+ super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
+ false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
+ TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
+ 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class));
+ }
+
+ @Override
+ protected void stopHalOperation() {
+
+ }
+
+ @Override
+ protected void startHalOperation() {
+
}
}
@@ -160,16 +209,15 @@
private boolean mUnableToStart;
private boolean mStarted;
- public TestClientMonitor(@NonNull Context context, @NonNull LazyDaemon<Object> lazyDaemon) {
- super(context, lazyDaemon, null /* token */, null /* listener */, 0 /* userId */,
- TAG, 0 /* cookie */, 0 /* sensorId */, 0 /* statsModality */,
- 0 /* statsAction */, 0 /* statsClient */);
+ public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ @NonNull LazyDaemon<Object> lazyDaemon) {
+ this(context, token, lazyDaemon, 0 /* cookie */);
}
- public TestClientMonitor(@NonNull Context context, @NonNull LazyDaemon<Object> lazyDaemon,
- int cookie) {
- super(context, lazyDaemon, null /* token */, null /* listener */, 0 /* userId */,
- TAG, cookie, 0 /* sensorId */, 0 /* statsModality */,
+ public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ @NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
+ super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
+ TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
0 /* statsAction */, 0 /* statsClient */);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 4a6e11b..bbb83b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -27,12 +27,13 @@
import android.platform.test.annotations.Presubmit;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.FastXmlSerializer;
import libcore.io.IoUtils;
@@ -43,16 +44,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -200,8 +198,7 @@
try {
fos = mSessionsFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_SESSIONS);
for (PackageInstallerSession session : sessions) {
@@ -227,8 +224,7 @@
FileInputStream fis = null;
try {
fis = mSessionsFile.openRead();
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(fis, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser in = Xml.resolvePullParser(fis);
int type;
while ((type = in.next()) != END_DOCUMENT) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 7108490..89c3d50 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.util.TypedXmlPullParser;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -38,7 +39,6 @@
import java.io.File;
import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -137,7 +137,7 @@
// first call to readXml should return the list with the expected signature, then the second
// call should reference this signature and complete successfully with no new entries in the
// List.
- XmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
+ TypedXmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
ArrayList<Signature> signatures = new ArrayList<>();
mPackageSetting.signatures.readXml(parser, signatures);
Set<String> expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
@@ -164,7 +164,7 @@
// If the cert tag key attribute does not contain a valid public key then a
// CertificateException should be thrown when attempting to build the SigningDetails; in
// this case the signing details should be set to UNKNOWN.
- XmlPullParser parser = getXMLFromResources(
+ TypedXmlPullParser parser = getXMLFromResources(
"xml/one-signer-invalid-public-key-cert-key.xml");
ArrayList<Signature> signatures = new ArrayList<>();
mPackageSetting.signatures.readXml(parser, signatures);
@@ -351,7 +351,7 @@
// When rotating the signing key a developer is able to specify the capabilities granted to
// the apps signed with the previous key. This test verifies a previous signing certificate
// with the flags set to 0 does not have any capabilities.
- XmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
+ TypedXmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
ArrayList<Signature> signatures = new ArrayList<>();
mPackageSetting.signatures.readXml(parser, signatures);
// obtain the Signature in the list matching the previous signing certificate
@@ -377,7 +377,7 @@
*/
private void verifyReadXmlReturnsExpectedSignatures(String xmlFile, int expectedSchemeVersion,
String... expectedSignatureValues) throws Exception {
- XmlPullParser parser = getXMLFromResources(xmlFile);
+ TypedXmlPullParser parser = getXMLFromResources(xmlFile);
ArrayList<Signature> signatures = new ArrayList<>();
mPackageSetting.signatures.readXml(parser, signatures);
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
@@ -394,7 +394,7 @@
*/
private void verifyReadXmlReturnsExpectedSignaturesAndLineage(String xmlFile,
int schemeVersion, String... expectedSignatureValues) throws Exception {
- XmlPullParser parser = getXMLFromResources(xmlFile);
+ TypedXmlPullParser parser = getXMLFromResources(xmlFile);
ArrayList<Signature> signatures = new ArrayList<>();
mPackageSetting.signatures.readXml(parser, signatures);
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
@@ -447,11 +447,10 @@
return result;
}
- private XmlPullParser getXMLFromResources(String xmlFile) throws Exception {
+ private TypedXmlPullParser getXMLFromResources(String xmlFile) throws Exception {
InputStream xmlStream = mContext.getResources().getAssets().open(
TEST_RESOURCES_FOLDER + "/" + xmlFile);
- XmlPullParser result = Xml.newPullParser();
- result.setInput(xmlStream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser result = Xml.resolvePullParser(xmlStream);
int type;
// advance the parser to the first tag
while ((type = result.next()) != XmlPullParser.START_TAG
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 8aa0e3fe..2cc9992 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -133,20 +133,16 @@
public void shouldVibrateForRingtones_withVibrateWhenRinging_onlyIgnoreSettingsForSilentMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
- assertEquals(AudioManager.RINGER_MODE_SILENT, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_MAX);
- assertEquals(AudioManager.RINGER_MODE_MAX, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_MAX);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
- assertEquals(AudioManager.RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
- assertEquals(AudioManager.RINGER_MODE_VIBRATE, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
}
@@ -155,20 +151,16 @@
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
- assertEquals(AudioManager.RINGER_MODE_SILENT, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_MAX);
- assertEquals(AudioManager.RINGER_MODE_MAX, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_MAX);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
- assertEquals(AudioManager.RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
- assertEquals(AudioManager.RINGER_MODE_VIBRATE, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
}
@@ -177,20 +169,16 @@
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
- assertEquals(AudioManager.RINGER_MODE_VIBRATE, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
- assertEquals(AudioManager.RINGER_MODE_SILENT, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_MAX);
- assertEquals(AudioManager.RINGER_MODE_MAX, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_MAX);
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
- assertEquals(AudioManager.RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
}
@@ -294,6 +282,10 @@
Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
- mAudioManager.reloadAudioSettings();
+ }
+
+ private void setRingerMode(int ringerMode) {
+ mAudioManager.setRingerModeInternal(ringerMode);
+ assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
}
}
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 87aaba2..70d3f98 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4923,6 +4923,32 @@
}
@Test
+ public void testDontCallShowToastAgainOnTheSameCustomToast() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ setAppInForegroundForToasts(mUid, true);
+
+ Binder token = new Binder();
+ ITransientNotification callback = mock(ITransientNotification.class);
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ // first time trying to show the toast, showToast gets called
+ nmService.enqueueToast(testPackage, token, callback, 2000, 0);
+ verify(callback, times(1)).show(any());
+
+ // second time trying to show the same toast, showToast isn't called again (total number of
+ // invocations stays at one)
+ nmService.enqueueToast(testPackage, token, callback, 2000, 0);
+ verify(callback, times(1)).show(any());
+ }
+
+ @Test
public void testAllowForegroundTextToasts() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
@@ -4959,6 +4985,33 @@
}
@Test
+ public void testDontCallShowToastAgainOnTheSameTextToast() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ setAppInForegroundForToasts(mUid, true);
+
+ Binder token = new Binder();
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ // first time trying to show the toast, showToast gets called
+ nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
+ verify(mStatusBar, times(1))
+ .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
+
+ // second time trying to show the same toast, showToast isn't called again (total number of
+ // invocations stays at one)
+ nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
+ verify(mStatusBar, times(1))
+ .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
+ }
+
+ @Test
public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws
Exception {
final String testPackage = "testPackageName";
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 26ee03c..f638660 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -53,6 +53,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -435,8 +437,7 @@
FileInputStream fis = null;
try {
fis = new FileInputStream(sSingleUserSettingsFile);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(fis);
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
@@ -470,8 +471,7 @@
FileInputStream stream = null;
try {
stream = mSettingsFile.openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
@@ -516,8 +516,7 @@
try {
fos = mSettingsFile.startWrite();
- FastXmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
true);
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index e20b1a4d..5874b4b 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -47,6 +47,8 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -355,8 +357,7 @@
mAccessoryPersistentPermissionMap.clear();
try (FileInputStream in = mPermissionsFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(in);
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
@@ -440,8 +441,7 @@
FileOutputStream out = null;
try {
out = mPermissionsFile.startWrite();
- FastXmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer serializer = Xml.resolveSerializer(out);
serializer.startDocument(null, true);
serializer.startTag(null, "permissions");
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 982e5f3..07de617 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -680,6 +680,7 @@
public static abstract class VideoCall {
/** @hide */
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void destroy();
/**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index b335a90..5bba747 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1238,7 +1238,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
@Deprecated
public List<PhoneAccountHandle> getPhoneAccountsForPackage() {
try {
@@ -1367,7 +1367,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void clearPhoneAccounts() {
clearAccounts();
}
@@ -1377,7 +1377,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void clearAccounts() {
try {
if (isServiceConnected()) {
@@ -1409,7 +1409,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public ComponentName getDefaultPhoneApp() {
try {
if (isServiceConnected()) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b806313..902dc06 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4792,7 +4792,7 @@
*/
@NonNull
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public static PersistableBundle getDefaultConfig() {
return new PersistableBundle(sDefaults);
}
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 8f5ec36..e595002 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -98,12 +98,14 @@
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void fillInNotifierBundle(Bundle bundle);
/**
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract boolean isEmpty();
@@ -111,6 +113,7 @@
* Invalidate this object. The location area code and the cell id are set to -1.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract void setStateInvalid();
/**
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 28feab2..42d7707 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -22,7 +22,12 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.telephony.BinderCacheManager;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+
+import com.android.internal.telephony.ITelephony;
/**
* Provides access to information about Telephony IMS services on the device.
@@ -30,8 +35,6 @@
@SystemService(Context.TELEPHONY_IMS_SERVICE)
public class ImsManager {
- private Context mContext;
-
/**
* <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the
* network due to the network returning a "forbidden" response. This may be due to a
@@ -87,6 +90,14 @@
public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
"android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ // Cache Telephony Binder interfaces, one cache per process.
+ private static final BinderCacheManager<ITelephony> sTelephonyCache =
+ new BinderCacheManager<>(ImsManager::getITelephonyInterface);
+ private static final BinderCacheManager<IImsRcsController> sRcsCache =
+ new BinderCacheManager<>(ImsManager::getIImsRcsControllerInterface);
+
+ private final Context mContext;
+
/**
* Use {@link Context#getSystemService(String)} to get an instance of this class.
* @hide
@@ -108,7 +119,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsRcsManager(mContext, subscriptionId);
+ return new ImsRcsManager(mContext, subscriptionId, sRcsCache);
}
/**
@@ -124,17 +135,19 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsMmTelManager(subscriptionId);
+ return new ImsMmTelManager(subscriptionId, sTelephonyCache);
}
/**
- * Create an instance of SipDelegateManager for the subscription id specified.
+ * Create an instance of {@link SipDelegateManager} for the subscription id specified.
* <p>
- * Used for RCS single registration cases, where an IMS application needs to forward SIP
- * traffic through the device's IMS service.
- * @param subscriptionId The ID of the subscription that this SipDelegateManager will use.
+ * Allows an IMS application to forward SIP traffic through the device's IMS service,
+ * which is used for cellular carriers that require the device to share a single IMS
+ * registration for both MMTEL and RCS features.
+ * @param subscriptionId The ID of the subscription that this {@link SipDelegateManager} will
+ * be bound to.
* @throws IllegalArgumentException if the subscription is invalid.
- * @return a SipDelegateManager instance for the specified subscription ID.
+ * @return a {@link SipDelegateManager} instance for the specified subscription ID.
* @hide
*/
@SystemApi
@@ -144,6 +157,22 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new SipDelegateManager(mContext, subscriptionId);
+ return new SipDelegateManager(mContext, subscriptionId, sRcsCache);
+ }
+
+ private static IImsRcsController getIImsRcsControllerInterface() {
+ return IImsRcsController.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get());
+ }
+
+ private static ITelephony getITelephonyInterface() {
+ return ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 96f3ce0..4eacad3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4661,7 +4661,7 @@
* be implemented instead.
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
}
@@ -4676,7 +4676,7 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
return false;
}
@@ -4695,7 +4695,7 @@
* @hide
*/
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
@Nullable
public Bundle getVisualVoicemailSettings(){
try {
@@ -8621,7 +8621,7 @@
/** @hide */
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public int checkCarrierPrivilegesForPackage(String pkgName) {
try {
ITelephony telephony = getITelephony();
@@ -8637,7 +8637,7 @@
/** @hide */
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
try {
ITelephony telephony = getITelephony();
@@ -8713,7 +8713,7 @@
/** @hide */
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void dial(String number) {
try {
ITelephony telephony = getITelephony();
@@ -8772,7 +8772,7 @@
*/
@Deprecated
@SystemApi
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void silenceRinger() {
// No-op
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index ff9329e..d58fa912 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -18,6 +18,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
import android.content.ContentValues;
import android.database.Cursor;
import android.hardware.radio.V1_5.ApnTypes;
@@ -26,6 +28,7 @@
import android.os.Parcelable;
import android.provider.Telephony;
import android.provider.Telephony.Carriers;
+import android.telephony.Annotation;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetworkType;
import android.telephony.ServiceState;
@@ -133,6 +136,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Skip464XlatStatus {}
+ /** @hide */
+ @StringDef(value = {
+ TYPE_ALL_STRING,
+ TYPE_CBS_STRING,
+ TYPE_DEFAULT_STRING,
+ TYPE_DUN_STRING,
+ TYPE_EMERGENCY_STRING,
+ TYPE_FOTA_STRING,
+ TYPE_HIPRI_STRING,
+ TYPE_IA_STRING,
+ TYPE_IMS_STRING,
+ TYPE_MCX_STRING,
+ TYPE_MMS_STRING,
+ TYPE_SUPL_STRING,
+ TYPE_XCAP_STRING,
+ }, prefix = "TYPE_", suffix = "_STRING")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApnTypeString {}
+
/**
* APN types for data connections. These are usage categories for an APN
* entry. One APN entry may support multiple APN types, eg, a single APN
@@ -140,99 +162,133 @@
* connections.<br/>
* APN_TYPE_ALL is a special type to indicate that this APN entry can
* service all data connections.
- * <p>
- * Note: The goal is to deprecate this. Due to the Carrier Table being used
- * directly, this isn't feasible right now.
*
* @hide
*/
+ @SystemApi
public static final String TYPE_ALL_STRING = "*";
/**
* APN type for default data traffic
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_DEFAULT_STRING = "default";
/**
- * APN type for MMS traffic
+ * APN type for MMS (Multimedia Messaging Service) traffic.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_MMS_STRING = "mms";
/**
- * APN type for SUPL assisted GPS
+ * APN type for SUPL (Secure User Plane Location) assisted GPS.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_SUPL_STRING = "supl";
/**
- * APN type for DUN traffic
+ * APN type for DUN (Dial-up networking) traffic
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_DUN_STRING = "dun";
/**
- * APN type for HiPri traffic
+ * APN type for high-priority traffic
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_HIPRI_STRING = "hipri";
/**
- * APN type for FOTA
+ * APN type for FOTA (Firmware over-the-air) traffic.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_FOTA_STRING = "fota";
/**
- * APN type for IMS
+ * APN type for IMS (IP Multimedia Subsystem) traffic.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_IMS_STRING = "ims";
/**
- * APN type for CBS
+ * APN type for CBS (Carrier Branded Services) traffic.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_CBS_STRING = "cbs";
/**
- * APN type for IA Initial Attach APN
+ * APN type for the IA (Initial Attach) APN
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_IA_STRING = "ia";
/**
* APN type for Emergency PDN. This is not an IA apn, but is used
* for access to carrier services in an emergency call situation.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_EMERGENCY_STRING = "emergency";
/**
- * APN type for Mission Critical Services
+ * APN type for Mission Critical Services.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_MCX_STRING = "mcx";
/**
- * APN type for XCAP
+ * APN type for XCAP (XML Configuration Access Protocol) traffic.
*
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
+ @SystemApi
public static final String TYPE_XCAP_STRING = "xcap";
@@ -1426,16 +1482,43 @@
}
/**
- * @param apnType APN type
- * @return APN type in string format
+ * Converts the integer representation of APN type to its string representation.
+ *
+ * @param apnType APN type as an integer
+ * @return String representation of the APN type, or an empty string if the provided integer is
+ * not a valid APN type.
* @hide
*/
- public static String getApnTypeString(int apnType) {
+ @SystemApi
+ public static @NonNull @ApnTypeString String getApnTypeString(@Annotation.ApnType int apnType) {
if (apnType == TYPE_ALL) {
return "*";
}
String apnTypeString = APN_TYPE_INT_MAP.get(apnType);
- return apnTypeString == null ? "Unknown" : apnTypeString;
+ return apnTypeString == null ? "" : apnTypeString;
+ }
+
+ /**
+ * Same as {@link #getApnTypeString(int)}, but returns "Unknown" instead of an empty string
+ * when provided with an invalid int for compatibility purposes.
+ * @hide
+ */
+ public static @NonNull String getApnTypeStringInternal(@Annotation.ApnType int apnType) {
+ String result = getApnTypeString(apnType);
+ return TextUtils.isEmpty(result) ? "Unknown" : result;
+ }
+
+ /**
+ * Converts the string representation of an APN type to its integer representation.
+ *
+ * @param apnType APN type as a string
+ * @return Integer representation of the APN type, or 0 if the provided string is not a valid
+ * APN type.
+ * @hide
+ */
+ @SystemApi
+ public static @Annotation.ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) {
+ return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(), 0);
}
/**
diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
new file mode 100644
index 0000000..beec4a6
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Callback interface provided to the SipTransport implementation to notify a remote application of
+ * the following:
+ * <ul>
+ * <li>A new incoming SIP message associated with the feature tags the SipDelegate registered
+ * with has been received or an in-dialog request to this SipDelegate has been received.</li>
+ * <li>Acknowledge that an outgoing SIP message from the RCS application has been sent
+ * successfully or notify the application of the reason why it was not sent</li>
+ * </ul>
+ * @hide
+ */
+public interface DelegateMessageCallback {
+
+ /**
+ * Send a new incoming SIP message to the remote application for processing.
+ */
+ void onMessageReceived(@NonNull SipMessage message);
+
+ /**
+ * Notify the remote application that a previous request to send a SIP message using
+ * {@link SipDelegate#sendMessage} has succeeded.
+ *
+ * @param viaTransactionId The transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ */
+ void onMessageSent(@NonNull String viaTransactionId);
+
+ /**
+ * Notify the remote application that a previous request to send a SIP message using
+ * {@link SipDelegate#sendMessage} has failed.
+ *
+ * @param viaTransactionId The Transaction ID found in the via header field of the previously
+ * sent {@link SipMessage}.
+ * @param reason The reason for the failure.
+ */
+ void onMessageSendFailure(@NonNull String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl
new file mode 100644
index 0000000..756ea92
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+parcelable DelegateRegistrationState;
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
new file mode 100644
index 0000000..4facfa7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the full state of the IMS feature tags associated with a SipDelegate and managed by the
+ * ImsService.
+ * @hide
+ */
+public final class DelegateRegistrationState implements Parcelable {
+
+ /**
+ * This feature tag has been deregistered for an unknown reason. Outgoing out-of-dialog SIP
+ * messages associated with feature tags that are not registered will fail.
+ */
+ public static final int DEREGISTERED_REASON_UNKNOWN = 0;
+
+ /**
+ * This feature tag has been deregistered because it is not provisioned to be used on this radio
+ * access technology or PDN. Outgoing out-of-dialog SIP messages associated with feature tags
+ * that are not registered will fail.
+ * <p>
+ * There may be new incoming SIP dialog requests on a feature that that is not provisioned. It
+ * is still expected that the SipDelegateConnection responds to the request.
+ */
+ public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1;
+
+ /**
+ * This feature tag has been deregistered because IMS has been deregistered. All outgoing SIP
+ * messages will fail until IMS registration occurs.
+ */
+ public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2;
+
+ /**
+ * This feature tag is being deregistered because the PDN that the IMS registration is on is
+ *changing.
+ * All open SIP dialogs need to be closed before the PDN change can proceed.
+ */
+ public static final int DEREGISTERING_REASON_PDN_CHANGE = 3;
+
+ /**
+ * This feature tag is being deregistered due to a provisioning change. This can be triggered by
+ * many things, such as a provisioning change triggered by the carrier network, a radio access
+ * technology change by the modem causing a different set of feature tags to be provisioned, or
+ * a user triggered hange, such as data being enabled/disabled.
+ * <p>
+ * All open SIP dialogs associated with the new deprovisioned feature tag need to be closed
+ * before the IMS registration modification can proceed.
+ */
+ public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4;
+
+ /**
+ * This feature tag is deregistering because the SipDelegate associated with this feature tag
+ * needs to change its supported feature set.
+ * <p>
+ * All open SIP Dialogs associated with this feature tag must be closed before this operation
+ * can proceed.
+ */
+ public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5;
+
+ /**
+ * This feature tag is deregistering because the SipDelegate is in the process of being
+ * destroyed.
+ * <p>
+ * All open SIP Dialogs associated with this feature tag must be closed before this operation
+ * can proceed.
+ */
+ public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DEREGISTERED_REASON_", value = {
+ DEREGISTERED_REASON_UNKNOWN,
+ DEREGISTERED_REASON_NOT_PROVISIONED,
+ DEREGISTERED_REASON_NOT_REGISTERED
+ })
+ public @interface DeregisteredReason {}
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DEREGISTERING_REASON_", value = {
+ DEREGISTERING_REASON_PDN_CHANGE,
+ DEREGISTERING_REASON_PROVISIONING_CHANGE,
+ DEREGISTERING_REASON_FEATURE_TAGS_CHANGING,
+ DEREGISTERING_REASON_DESTROY_PENDING
+ })
+ public @interface DeregisteringReason {}
+
+ private final ArrayList<String> mRegisteredTags = new ArrayList<>();
+ private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>();
+ private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>();
+
+ /**
+ * Builder used to create new instances of {@link DelegateRegistrationState}.
+ */
+ public static class Builder {
+
+ private final DelegateRegistrationState mState;
+
+ /* Create a new instance of {@link Builder} */
+ public Builder() {
+ mState = new DelegateRegistrationState();
+ }
+
+ /**
+ * Add a feature tag that is currently included in the current network IMS Registration.
+ * @param featureTag The IMS media feature tag included in the current IMS registration.
+ * @return The in-progress Builder instance for RegistrationState.
+ */
+ public Builder addRegisteredFeatureTag(@NonNull String featureTag) {
+ if (!mState.mRegisteredTags.contains(featureTag)) {
+ mState.mRegisteredTags.add(featureTag);
+ }
+ return this;
+ }
+
+ /**
+ * Add a list of feature tags that are currently included in the current network IMS
+ * Registration.
+ * @param featureTags The IMS media feature tags included in the current IMS registration.
+ * @return The in-progress Builder instance for RegistrationState.
+ */
+ public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
+ mState.mRegisteredTags.addAll(featureTags);
+ return this;
+ }
+
+ /**
+ * Add a feature tag that is in the current network IMS Registration, but is in the progress
+ * of being deregistered and requires action from the RCS application before the IMS
+ * registration can be modified.
+ *
+ * See {@link DeregisteringReason} for more information regarding what is required by the
+ * RCS application to proceed.
+ *
+ * @param featureTag The media feature tag that has limited or no availability due to its
+ * current deregistering state.
+ * @param reason The reason why the media feature tag has moved to the deregistering state.
+ * The availability of the feature tag depends on the {@link DeregisteringReason}.
+ * @return The in-progress Builder instance for RegistrationState.
+ */
+ public Builder addDeregisteringFeatureTag(@NonNull String featureTag,
+ @DeregisteringReason int reason) {
+ boolean ftExists = mState.mDeregisteringTags.stream().anyMatch(
+ f -> f.getFeatureTag().equals(featureTag));
+ if (!ftExists) {
+ mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
+ }
+ return this;
+ }
+
+ /**
+ * Add a feature tag that is currently not included in the network RCS registration. See
+ * {@link DeregisteredReason} for more information regarding the reason for why the feature
+ * tag is not registered.
+ * @param featureTag The media feature tag that is not registered.
+ * @param reason The reason why the media feature tag has been deregistered.
+ * @return The in-progress Builder instance for RegistrationState.
+ */
+ public Builder addDeregisteredFeatureTag(@NonNull String featureTag,
+ @DeregisteredReason int reason) {
+ boolean ftExists = mState.mDeregisteredTags.stream().anyMatch(
+ f -> f.getFeatureTag().equals(featureTag));
+ if (!ftExists) {
+ mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
+ }
+ return this;
+ }
+
+ /**
+ * @return the finalized instance.
+ */
+ public DelegateRegistrationState build() {
+ return mState;
+ }
+ }
+
+ /**
+ * The builder should be used to construct a new instance of this class.
+ */
+ private DelegateRegistrationState() {}
+
+ /**
+ * Used for unparcelling only.
+ */
+ private DelegateRegistrationState(Parcel source) {
+ source.readList(mRegisteredTags, null /*classloader*/);
+ readStateFromParcel(source, mDeregisteringTags);
+ readStateFromParcel(source, mDeregisteredTags);
+ }
+
+ /**
+ * Get the feature tags that this SipDelegate is associated with that are currently part of the
+ * network IMS registration. SIP Messages both in and out of a SIP Dialog may be sent and
+ * received using these feature tags.
+ * @return A Set of feature tags that the SipDelegate has associated with that are included in
+ * the network IMS registration.
+ */
+ public @NonNull Set<String> getRegisteredFeatureTags() {
+ return new ArraySet<>(mRegisteredTags);
+ }
+
+ /**
+ * Get the feature tags that this SipDelegate is associated with that are currently part of the
+ * network IMS registration but are in the process of being deregistered.
+ * <p>
+ * Any incoming SIP messages associated with a feature tag included in this list will still be
+ * delivered. Outgoing SIP messages that are still in-dialog will be delivered to the
+ * SipDelegate, but outgoing out-of-dialog SIP messages with a feature tag that is included in
+ * this list will fail.
+ * <p>
+ * The SipDelegate will stay in this state for a limited period of time while it waits for the
+ * RCS application to perform a specific action. More details on the actions that can cause this
+ * state as well as the expected response are included in the reason codes and can be found in
+ * {@link DeregisteringReason}.
+ * @return A Set of feature tags that the SipDelegate has associated with that are included in
+ * the network IMS registration but are in the process of deregistering.
+ */
+ public @NonNull Set<FeatureTagState> getDeregisteringFeatureTags() {
+ return new ArraySet<>(mDeregisteringTags);
+ }
+
+ /**
+ * Get the list of feature tags that are associated with this SipDelegate but are not currently
+ * included in the network IMS registration.
+ * <p>
+ * See {@link DeregisteredReason} codes for more information related to the reasons why this may
+ * occur.
+ * <p>
+ * Due to network race conditions, there may still be onditions where an incoming out-of-dialog
+ * SIP message is delivered for a feature tag that is considered deregistered. Due to this
+ * condition, in-dialog outgoing SIP messages for deregistered feature tags will still be
+ * allowed as long as they are in response to a dialog started by a remote party. Any outgoing
+ * out-of-dialog SIP messages associated with feature tags included in this list will fail to be
+ * sent.
+ * @return A list of feature tags that the SipDelegate has associated with that not included in
+ * the network IMS registration.
+ */
+ public @NonNull Set<FeatureTagState> getDeregisteredFeatureTags() {
+ return new ArraySet<>(mDeregisteredTags);
+ }
+
+ public static final Creator<DelegateRegistrationState> CREATOR =
+ new Creator<DelegateRegistrationState>() {
+ @Override
+ public DelegateRegistrationState createFromParcel(Parcel source) {
+ return new DelegateRegistrationState(source);
+ }
+
+ @Override
+ public DelegateRegistrationState[] newArray(int size) {
+ return new DelegateRegistrationState[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeList(mRegisteredTags);
+ writeStateToParcel(dest, mDeregisteringTags);
+ writeStateToParcel(dest, mDeregisteredTags);
+ }
+
+ private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) {
+ dest.writeInt(state.size());
+ for (FeatureTagState s : state) {
+ dest.writeString(s.getFeatureTag());
+ dest.writeInt(s.getState());
+ }
+ }
+
+ private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) {
+ int len = source.readInt();
+ for (int i = 0; i < len; i++) {
+ String ft = source.readString();
+ int reason = source.readInt();
+ emptyState.add(new FeatureTagState(ft, reason));
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DelegateRegistrationState that = (DelegateRegistrationState) o;
+ return mRegisteredTags.equals(that.mRegisteredTags)
+ && mDeregisteringTags.equals(that.mDeregisteringTags)
+ && mDeregisteredTags.equals(that.mDeregisteredTags);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.aidl b/telephony/java/android/telephony/ims/DelegateRequest.aidl
new file mode 100644
index 0000000..60c990f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+parcelable DelegateRequest;
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java
new file mode 100644
index 0000000..f384901
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRequest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains information required for the creation of a {@link SipDelegate} and the associated
+ * SipDelegateConnection given back to the requesting application.
+ * @hide
+ */
+public final class DelegateRequest implements Parcelable {
+
+ private final ArrayList<String> mFeatureTags;
+
+ /**
+ * Create a new DelegateRequest, which will be used to create a SipDelegate by the ImsService.
+ * @param featureTags The list of IMS feature tags that will be associated with the SipDelegate
+ * created using this DelegateRequest. All feature tags are expected to be in
+ * the format defined in RCC.07 section 2.6.1.3.
+ */
+ public DelegateRequest(@NonNull Set<String> featureTags) {
+ if (featureTags == null) {
+ throw new IllegalStateException("Invalid arguments, featureTags List can not be null");
+ }
+ mFeatureTags = new ArrayList<>(featureTags);
+ }
+
+ /**
+ * @return the list of IMS feature tag associated with this DelegateRequest in the format
+ * defined in RCC.07 section 2.6.1.3.
+ */
+ public Set<String> getFeatureTags() {
+ return new ArraySet<>(mFeatureTags);
+ }
+
+ /**
+ * Internal constructor used only for unparcelling.
+ */
+ private DelegateRequest(Parcel in) {
+ mFeatureTags = new ArrayList<>();
+ in.readList(mFeatureTags, null /*classLoader*/);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeList(mFeatureTags);
+ }
+
+ public static final @NonNull Creator<DelegateRequest> CREATOR = new Creator<DelegateRequest>() {
+ @Override
+ public DelegateRequest createFromParcel(Parcel source) {
+ return new DelegateRequest(source);
+ }
+
+ @Override
+ public DelegateRequest[] newArray(int size) {
+ return new DelegateRequest[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DelegateRequest that = (DelegateRequest) o;
+ return mFeatureTags.equals(that.mFeatureTags);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFeatureTags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
new file mode 100644
index 0000000..0f1afc4
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.ims.stub.SipDelegate;
+import android.telephony.ims.stub.SipTransportImplBase;
+
+import java.util.List;
+
+/**
+ * Callback interface to notify a remote application of the following:
+ * <ul>
+ * <li>the {@link SipDelegate} associated with this callback has been created or destroyed in
+ * response to a creation or destruction request from the framework</li>
+ * <li>the SIP IMS configuration associated with this {@link SipDelegate} has changed</li>
+ * <li>the IMS registration of the feature tags associated with this {@link SipDelegate} have
+ * changed.</li>
+ * </ul>
+ * @hide
+ */
+public interface DelegateStateCallback {
+
+ /**
+ * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is
+ * called by the framework to notify the framework and remote application that the
+ * {@link SipDelegate} has been successfully created.
+ *
+ * @param delegate The SipDelegate created to service the DelegateRequest.
+ * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags
+ * associated with this {@link SipDelegate} that have no access to send/receive SIP messages
+ * as well as a reason for why the feature tag is denied. For more information on the reason
+ * why the feature tag was denied access, see the
+ * {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due
+ * to this {@link SipDelegate} not supporting a feature or this ImsService already
+ * implementing this feature elsewhere. If all features of this {@link SipDelegate} are
+ * denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will
+ * later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the
+ * delegate up.
+ */
+ void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags);
+
+ /**
+ * This must be called by the ImsService after the framework calls
+ * {@link SipTransportImplBase#destroySipDelegate} to notify the framework and remote
+ * application that the procedure to destroy the {@link SipDelegate} has been completed.
+ * @param reasonCode The reason for closing this delegate.
+ */
+ void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reasonCode);
+
+ /**
+ * Call to notify the remote application of a configuration change associated with this
+ * {@link SipDelegate}.
+ * <p>
+ * The remote application will not be able to proceed sending SIP messages until after this
+ * configuration is sent the first time, so this configuration should be sent as soon as the
+ * {@link SipDelegate} has access to these configuration parameters.
+ * <p>
+ * Incoming SIP messages should not be routed to the remote application until AFTER this
+ * configuration change is sent to ensure that the remote application can respond correctly.
+ * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+ * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+ * change event to reduce conditions where the remote application is using a stale IMS
+ * configuration.
+ */
+ void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config);
+
+ /**
+ * Call to notify the remote application that the {@link SipDelegate} has modified the IMS
+ * registration state of the RCS feature tags that were requested as part of the initial
+ * {@link DelegateRequest}.
+ * <p>
+ * See {@link DelegateRegistrationState} for more information about how IMS Registration state
+ * should be communicated the associated SipDelegateConnection in cases such as
+ * IMS deregistration, handover, PDN change, provisioning changes, etc…
+ * <p>
+ * Note: Even after the status of the feature tags are updated here to deregistered, the
+ * SipDelegate must still be able to handle these messages and call
+ * {@link DelegateMessageCallback#onMessageSendFailure} to notify the RCS application that the
+ * message was not sent.
+ *
+ * @param registrationState The current network IMS registration state for all feature tags
+ * associated with this SipDelegate.
+ */
+ void onFeatureTagRegistrationChanged(@NonNull DelegateRegistrationState registrationState);
+}
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl
new file mode 100644
index 0000000..bce5574
--- /dev/null
+++ b/telephony/java/android/telephony/ims/FeatureTagState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+parcelable FeatureTagState;
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java
new file mode 100644
index 0000000..060be6f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/FeatureTagState.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Maps an IMS media feature tag 3gpp universal resource name (URN) previously mapped to a
+ * {@link SipDelegate} in the associated {@link DelegateRequest} to its current availability
+ * state as set by the ImsService managing the related IMS registration.
+ *
+ * This class is only used to report more information about a IMS feature tag that is not fully
+ * available at this time.
+ * <p>
+ * Please see {@link DelegateRegistrationState}, {@link DelegateStateCallback}, and
+ * {@link DelegateConnectionStateCallback} for more information about how this class is used to
+ * convey the state of IMS feature tags that were requested by {@link DelegateRequest} but are not
+ * currently available.
+ * @hide
+ */
+public final class FeatureTagState implements Parcelable {
+
+ private final String mFeatureTag;
+ private final int mState;
+
+ /**
+ * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState}
+ * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged(
+ * DelegateRegistrationState, List)} and
+ * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this
+ * is used.
+ *
+ * @param featureTag The IMS feature tag that is deregistered, in the process of
+ * deregistering, or denied.
+ * @param state The {@link DelegateRegistrationState.DeregisteredReason},
+ * {@link DelegateRegistrationState.DeregisteringReason}, or
+ * {@link SipDelegateManager.DeniedReason} associated with this feature tag.
+ */
+ public FeatureTagState(@NonNull String featureTag, int state) {
+ mFeatureTag = featureTag;
+ mState = state;
+ }
+
+ /**
+ * Used for constructing instances during un-parcelling.
+ */
+ private FeatureTagState(Parcel source) {
+ mFeatureTag = source.readString();
+ mState = source.readInt();
+ }
+
+ /**
+ * @return The IMS feature tag string that is in the process of deregistering,
+ * deregistered, or denied.
+ */
+ public @NonNull String getFeatureTag() {
+ return mFeatureTag;
+ }
+
+ /**
+ * @return The reason for why the feature tag is currently in the process of deregistering,
+ * has been deregistered, or has been denied. See {@link DelegateRegistrationState} and
+ * {@link DelegateConnectionStateCallback} for more information.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mFeatureTag);
+ dest.writeInt(mState);
+ }
+
+ public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
+ @Override
+ public FeatureTagState createFromParcel(Parcel source) {
+ return new FeatureTagState(source);
+ }
+
+ @Override
+ public FeatureTagState[] newArray(int size) {
+ return new FeatureTagState[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FeatureTagState that = (FeatureTagState) o;
+ return mState == that.mState
+ && mFeatureTag.equals(that.mFeatureTag);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFeatureTag, mState);
+ }
+
+ @Override
+ public String toString() {
+ return "FeatureTagState{" + "mFeatureTag='" + mFeatureTag + ", mState=" + mState + '}';
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9a55cec..1b51936 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -450,8 +450,6 @@
/** Indicates if we have known the intent of the user for the call is emergency */
private boolean mHasKnownUserIntentEmergency = false;
- private Set<RtpHeaderExtensionType> mOfferedRtpHeaderExtensionTypes = new ArraySet<>();
-
private Set<RtpHeaderExtensionType> mAcceptedRtpHeaderExtensionTypes = new ArraySet<>();
/**
@@ -692,7 +690,6 @@
out.writeBoolean(mHasKnownUserIntentEmergency);
out.writeInt(mRestrictCause);
out.writeInt(mCallerNumberVerificationStatus);
- out.writeArray(mOfferedRtpHeaderExtensionTypes.toArray());
out.writeArray(mAcceptedRtpHeaderExtensionTypes.toArray());
}
@@ -708,9 +705,6 @@
mHasKnownUserIntentEmergency = in.readBoolean();
mRestrictCause = in.readInt();
mCallerNumberVerificationStatus = in.readInt();
- Object[] offered = in.readArray(RtpHeaderExtensionType.class.getClassLoader());
- mOfferedRtpHeaderExtensionTypes = Arrays.stream(offered)
- .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader());
mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted)
.map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
@@ -1106,46 +1100,13 @@
}
/**
- * For an incoming or outgoing call, indicates the {@link RtpHeaderExtensionType}s which the
- * caller is offering to make available.
- * <p>
- * For outgoing calls, an {@link ImsService} will reserve
- * {@link RtpHeaderExtensionType#getLocalIdentifier()} identifiers the telephony stack has
- * proposed to use and not use these same local identifiers. The offered header extension
- * types for an outgoing call can be found in the
- * {@link ImsCallProfile#getOfferedRtpHeaderExtensionTypes()} and will be available to the
- * {@link ImsService} in {@link MmTelFeature#createCallSession(ImsCallProfile)}.
- * The {@link ImsService} sets the accepted {@link #setAcceptedRtpHeaderExtensionTypes(Set)}
- * when the SDP offer/accept process has completed.
- * <p>
- * According to RFC8285, RTP header extensions available to a call are determined using the
- * offer/accept phase of the SDP protocol (see RFC4566).
- *
- * @return the {@link RtpHeaderExtensionType}s which were offered by other end of the call.
- */
- public @NonNull Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() {
- return mOfferedRtpHeaderExtensionTypes;
- }
-
- /**
- * Sets the offered {@link RtpHeaderExtensionType}s for this call.
- * <p>
- * According to RFC8285, RTP header extensions available to a call are determined using the
- * offer/accept phase of the SDP protocol (see RFC4566).
- *
- * @param rtpHeaderExtensions the {@link RtpHeaderExtensionType}s which are offered.
- */
- public void setOfferedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType>
- rtpHeaderExtensions) {
- mOfferedRtpHeaderExtensionTypes.clear();
- mOfferedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions);
- }
-
- /**
* Gets the {@link RtpHeaderExtensionType}s which have been accepted by both ends of the call.
* <p>
* According to RFC8285, RTP header extensions available to a call are determined using the
* offer/accept phase of the SDP protocol (see RFC4566).
+ * <p>
+ * The offered header extension types supported by the framework and exposed to the
+ * {@link ImsService} via {@link MmTelFeature#changeOfferedRtpHeaderExtensionTypes(Set)}.
*
* @return the {@link RtpHeaderExtensionType}s which were accepted by the other end of the call.
*/
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 76c1faf..218875e3 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
@@ -213,6 +214,7 @@
}
private final int mSubId;
+ private final BinderCacheManager<ITelephony> mBinderCache;
/**
* Create an instance of {@link ImsMmTelManager} for the subscription id specified.
@@ -242,7 +244,8 @@
throw new IllegalArgumentException("Invalid subscription ID");
}
- return new ImsMmTelManager(subId);
+ return new ImsMmTelManager(subId, new BinderCacheManager<>(
+ ImsMmTelManager::getITelephonyInterface));
}
/**
@@ -250,8 +253,9 @@
* @hide
*/
@VisibleForTesting
- public ImsMmTelManager(int subId) {
+ public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) {
mSubId = subId;
+ mBinderCache = binderCache;
}
/**
@@ -1367,7 +1371,11 @@
}
}
- private static ITelephony getITelephony() {
+ private ITelephony getITelephony() {
+ return mBinderCache.getBinder();
+ }
+
+ private static ITelephony getITelephonyInterface() {
ITelephony binder = ITelephony.Stub.asInterface(
TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 5a32075..074aefe 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -28,6 +28,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -149,14 +150,17 @@
private final int mSubId;
private final Context mContext;
+ private final BinderCacheManager<IImsRcsController> mBinderCache;
/**
* Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
* @hide
*/
- public ImsRcsManager(Context context, int subId) {
+ public ImsRcsManager(Context context, int subId,
+ BinderCacheManager<IImsRcsController> binderCache) {
mSubId = subId;
mContext = context;
+ mBinderCache = binderCache;
}
/**
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
new file mode 100644
index 0000000..6bfdc2c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Represents a connection to the remote {@link SipDelegate} that is managed by the
+ * {@link ImsService} implementing IMS for the subscription that is associated with it.
+ * <p>
+ * The remote delegate will handle messages sent by this {@link SipDelegateConnection}, notifying
+ * the associated {@link DelegateMessageCallback} when the message was either sent successfully or
+ * failed to be sent.
+ * <p>
+ * It is also the responsibility of this {@link SipDelegateConnection} to acknowledge when incoming
+ * SIP messages have been received successfully via
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} or when there was an error
+ * receiving the message using {@link #notifyMessageReceived(String)} and
+ * {@link #notifyMessageReceiveError(String, int)}.
+ *
+ * @see SipDelegateManager#createSipDelegate
+ * @hide
+ */
+public interface SipDelegateConnection {
+
+ /**
+ * Send a SIP message to the SIP delegate to be sent over the carrier’s network. The
+ * {@link SipMessage} will either be acknowledged with
+ * {@link DelegateMessageCallback#onMessageSent(String)} upon successful sending of this message
+ * or {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if there was an error
+ * sending the message.
+ * @param sipMessage The SipMessage to be sent.
+ * @param configVersion The SipDelegateImsConfiguration version used to construct the
+ * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
+ * information on this parameter and why it is used.
+ */
+ void sendMessage(@NonNull SipMessage sipMessage, int configVersion);
+
+ /**
+ * Notify the {@link SipDelegate} that a SIP message received from
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} has been received successfully
+ * and is being processed.
+ * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+ * branch parameter.
+ */
+ void notifyMessageReceived(@NonNull String viaTransactionId);
+
+ /**
+ * Notify the SIP delegate that the SIP message has been received from
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)}, however there was an error
+ * processing it.
+ * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+ * branch parameter.
+ * @param reason The reason why the error occurred.
+ */
+ void notifyMessageReceiveError(@NonNull String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl
new file mode 100644
index 0000000..44ae1b1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+parcelable SipDelegateImsConfiguration;
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
new file mode 100644
index 0000000..8abd0ee
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
+ * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
+ * <p>
+ * The IMS service must generate new instances of this configuration as the IMS configuration
+ * managed by the IMS service changes. Along with each {@link SipDelegateImsConfiguration} instance
+ * containing the configuration is the "version", which should be incremented every time a new
+ * {@link SipDelegateImsConfiguration} instance is created. The {@link SipDelegateConnection} will
+ * include the version of the {@link SipDelegateImsConfiguration} instance that it used in order for
+ * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
+ * to generate the {@link SipMessage} and return
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
+ * regenerate that {@link SipMessage} using the correct {@link SipDelegateImsConfiguration}
+ * instance.
+ * <p>
+ * Every time the IMS configuration state changes in the IMS service, a full configuration should
+ * be generated. The new {@link SipDelegateImsConfiguration} instance should not be an incremental
+ * update.
+ * @hide
+ */
+public class SipDelegateImsConfiguration implements Parcelable {
+
+ /**
+ * IPV4 Address type.
+ * <p>
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+ */
+ public static final String IPTYPE_IPV4 = "IPV4";
+
+ /**
+ * IPV6 Address type.
+ * <p>
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+ */
+ public static final String IPTYPE_IPV6 = "IPV6";
+
+ /**
+ * The SIP transport uses UDP.
+ * <p>
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+ */
+ public static final String SIP_TRANSPORT_UDP = "UDP";
+
+ /**
+ * The SIP transport uses TCP.
+ * <p>
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+ */
+ public static final String SIP_TRANSPORT_TCP = "TCP";
+
+ /**
+ * Flag specifying if SIP compact form is enabled
+ */
+ public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL =
+ "sip_config_is_compact_form_enabled_bool";
+
+ /**
+ * Flag specifying if SIP keepalives are enabled
+ */
+ public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL =
+ "sip_config_is_keepalive_enabled_bool";
+
+ /**
+ * Maximum SIP payload to be sent on UDP. If the SIP message payload is greater than max udp
+ * payload size, then TCP must be used
+ */
+ public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT =
+ "sip_config_udp_max_payload_size_int";
+
+ /**
+ * Transport protocol used for SIP signaling.
+ * Available options are: {@link #SIP_TRANSPORT_UDP }, {@link #SIP_TRANSPORT_TCP }
+ */
+ public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING =
+ "sip_config_protocol_type_string";
+
+ /**
+ * IMS public user identifier string
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING =
+ "sip_config_ue_public_user_id_string";
+
+ /**
+ * IMS private user identifier string
+ */
+ public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING =
+ "sip_config_ue_private_user_id_string";
+
+ /**
+ * IMS home domain string
+ */
+ public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+
+ /**
+ * IMEI string. Application can include the Instance-ID feature tag " +sip.instance" in the
+ * Contact header with a value of the device IMEI in the form "urn:gsma:imei:<device IMEI>".
+ */
+ public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+
+ /**
+ * IP address type for SIP signaling.
+ * Available options are: {@link #IPTYPE_IPV6}, {@link #IPTYPE_IPV4}
+ */
+ public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+
+ /**
+ * Local IPaddress used for SIP signaling.
+ */
+ public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING =
+ "sip_config_ue_default_ipaddress_string";
+
+ /**
+ * Local port used for sending SIP traffic
+ */
+ public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT =
+ "sip_config_ue_default_port_int";
+
+ /**
+ * SIP server / PCSCF default ip address
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING =
+ "sip_config_server_default_ipaddress_string";
+
+ /**
+ * SIP server / PCSCF port used for sending SIP traffic
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT =
+ "sip_config_server_default_port_int";
+
+ /**
+ * Flag specifying if Network Address Translation is enabled and UE is behind a NAT.
+ */
+ public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL =
+ "sip_config_is_nat_enabled_bool";
+
+ /**
+ * UE's public IPaddress when UE is behind a NAT.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING =
+ "sip_config_ue_public_ipaddress_with_nat_string";
+
+ /**
+ * UE's public SIP port when UE is behind a NAT.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT =
+ "sip_config_ue_public_port_with_nat_int";
+
+ /**
+ * Flag specifying if Globally routable user-agent uri (GRUU) is enabled as per TS 23.808
+ */
+ public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL =
+ "sip_config_is_gruu_enabled_bool";
+
+ /**
+ * UE's Globally routable user-agent uri if this feature is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING =
+ "sip_config_ue_public_gruu_string";
+
+ /**
+ * Flag specifying if SIP over IPSec is enabled.
+ */
+ public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL =
+ "sip_config_is_ipsec_enabled_bool";
+ /**
+ * UE's SIP port used to send traffic when IPSec is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT =
+ "sip_config_ue_ipsec_client_port_int";
+
+ /**
+ * UE's SIP port used to receive traffic when IPSec is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT =
+ "sip_config_ue_ipsec_server_port_int";
+
+ /**
+ * UE's SIP port used for the previous IPsec security association if IPSec is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT =
+ "sip_config_ue_ipsec_old_client_port_int";
+
+ /**
+ * Port number used by the SIP server to send SIP traffic when IPSec is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT =
+ "sip_config_server_ipsec_client_port_int";
+
+ /**
+ * Port number used by the SIP server to receive incoming SIP traffic when IPSec is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT =
+ "sip_config_server_ipsec_server_port_int";
+
+ /**
+ * Port number used by the SIP server to send SIP traffic on the previous IPSec security
+ * association when IPSec is enabled.
+ * <p>
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT =
+ "sip_config_server_ipsec_old_client_port_int";
+ /**
+ * SIP Authentication header string
+ */
+ public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING =
+ "sip_config_auhentication_header_string";
+
+ /**
+ * SIP Authentication nonce string
+ */
+ public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING =
+ "sip_config_authentication_nonce_string";
+
+ /**
+ * SIP service route header string
+ */
+ public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING =
+ "sip_config_service_route_header_string";
+
+ /**
+ * SIP security verify header string
+ */
+ public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING =
+ "sip_config_security_verify_header_string";
+
+ /**
+ * SIP Path header string
+ */
+ public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING =
+ "sip_config_path_header_string";
+
+ /**
+ * SIP User part string in contact header
+ */
+ public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING =
+ "sip_config_uri_user_part_string";
+
+ /**
+ * SIP P-access-network-info header string
+ */
+ public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING =
+ "sip_config_p_access_network_info_header_string";
+
+ /**
+ * SIP P-last-access-network-info header string
+ */
+ public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING =
+ "sip_config_p_last_access_network_info_header_string";
+
+ /**
+ * SIP P-associated-uri header string
+ */
+ public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING =
+ "sip_config_p_associated_uri_header_string";
+
+ /**@hide*/
+ @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_STRING", value = {
+ KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING,
+ KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING,
+ KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING,
+ KEY_SIP_CONFIG_HOME_DOMAIN_STRING,
+ KEY_SIP_CONFIG_IMEI_STRING,
+ KEY_SIP_CONFIG_IPTYPE_STRING,
+ KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING,
+ KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING,
+ KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING,
+ KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING,
+ KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING,
+ KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING,
+ KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
+ KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
+ KEY_SIP_CONFIG_PATH_HEADER_STRING,
+ KEY_SIP_CONFIG_URI_USER_PART_STRING,
+ KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING,
+ KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING,
+ KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StringConfigKey {}
+
+ /**@hide*/
+ @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_INT", value = {
+ KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
+ KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT,
+ KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT,
+ KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT,
+ KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT,
+ KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IntConfigKey {}
+
+ /**@hide*/
+ @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_BOOL", value = {
+ KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BooleanConfigKey {}
+
+ /**
+ * Builder class to be used when constructing a new SipDelegateImsConfiguration.
+ */
+ public static class Builder {
+ private final long mVersion;
+ private final PersistableBundle mBundle;
+
+ /**
+ * Creates an empty implementation of SipDelegateImsConfiguration.
+ * @param version The version associated with the SipDelegateImsConfiguration being built.
+ * See {@link #getVersion} for more information.
+ */
+ public Builder(int version) {
+ mVersion = version;
+ mBundle = new PersistableBundle();
+ }
+ /**
+ * Clones an existing implementation of SipDelegateImsConfiguration to handle situations
+ * where only a small number of parameters have changed from the previous configuration.
+ * <p>
+ * Automatically increments the version of this configuration by 1. See {@link #getVersion}
+ * for more information.
+ */
+ public Builder(@NonNull SipDelegateImsConfiguration config) {
+ mVersion = config.getVersion() + 1;
+ mBundle = config.copyBundle();
+ }
+ /**
+ * Put a string value into this configuration bundle for the given key.
+ */
+ public Builder putString(@StringConfigKey String key, String value) {
+ mBundle.putString(key, value);
+ return this;
+ }
+
+ /**
+ * Replace the existing default value with a new value for a given key.
+ */
+ public Builder putInt(@IntConfigKey String key, int value) {
+ mBundle.putInt(key, value);
+ return this;
+ }
+
+ /**
+ * Replace the existing default value with a new value for a given key.
+ */
+ public Builder putBoolean(@BooleanConfigKey String key, boolean value) {
+ mBundle.putBoolean(key, value);
+ return this;
+ }
+
+ /**
+ * @return a new SipDelegateImsConfiguration from this Builder.
+ */
+ public SipDelegateImsConfiguration build() {
+ return new SipDelegateImsConfiguration(mVersion, mBundle);
+ }
+ }
+
+ private final long mVersion;
+ private final PersistableBundle mBundle;
+
+ private SipDelegateImsConfiguration(long version, PersistableBundle bundle) {
+ mVersion = version;
+ mBundle = bundle;
+ }
+
+ private SipDelegateImsConfiguration(Parcel source) {
+ mVersion = source.readLong();
+ mBundle = source.readPersistableBundle();
+ }
+
+ /**
+ * @return the string value associated with a given key or {@code null} if it doesn't exist.
+ */
+ public @StringConfigKey String getString(String key) {
+ return mBundle.getString(key);
+ }
+
+ /**
+ * @return the Integer value associated with a given key or {@code null} if the value doesn't
+ * exist.
+ */
+ public @IntConfigKey Integer getInt(String key) {
+ if (!mBundle.containsKey(key)) {
+ return null;
+ }
+ return mBundle.getInt(key);
+ }
+
+ /**
+ * @return the Integer value associated with a given key or {@code null} if the value doesn't
+ * exist.
+ */
+ public @BooleanConfigKey Boolean getBoolen(String key) {
+ if (!mBundle.containsKey(key)) {
+ return null;
+ }
+ return mBundle.getBoolean(key);
+ }
+
+ /**
+ * @return a shallow copy of the full configuration.
+ */
+ public PersistableBundle copyBundle() {
+ return new PersistableBundle(mBundle);
+ }
+
+ /**
+ * An integer representing the version number of this SipDelegateImsConfiguration.
+ * {@link SipMessage}s that are created using this configuration will also have a this
+ * version number associated with them, which will allow the IMS service to validate that the
+ * {@link SipMessage} was using the latest configuration during creation and not a stale
+ * configuration due to race conditions between the configuration being updated and the RCS
+ * application not receiving the updated configuration before generating a new message.
+ *
+ * @return the version number associated with this {@link SipDelegateImsConfiguration}.
+ */
+ public long getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mVersion);
+ dest.writePersistableBundle(mBundle);
+ }
+
+ public static final Creator<SipDelegateImsConfiguration> CREATOR =
+ new Creator<SipDelegateImsConfiguration>() {
+ @Override
+ public SipDelegateImsConfiguration createFromParcel(Parcel source) {
+ return new SipDelegateImsConfiguration(source);
+ }
+
+ @Override
+ public SipDelegateImsConfiguration[] newArray(int size) {
+ return new SipDelegateImsConfiguration[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 82c8a9c..337b7d4 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -17,29 +17,251 @@
package android.telephony.ims;
import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.telephony.BinderCacheManager;
import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
/**
- * Manages the creation and destruction of SipDelegates, which allow an IMS application to forward
- * SIP messages for the purposes of providing a single IMS registration to the carrier's IMS network
- * from multiple sources.
+ * Manages the creation and destruction of SipDelegates for the {@link ImsService} managing IMS
+ * for the subscription ID that this SipDelegateManager has been created for.
+ *
+ * This allows multiple IMS applications to forward SIP messages to/from their application for the
+ * purposes of providing a single IMS registration to the carrier's IMS network from potentially
+ * many IMS stacks implementing a subset of the supported MMTEL/RCS features.
* @hide
*/
@SystemApi
public class SipDelegateManager {
+ /**
+ * The SIP message has failed being sent or received for an unknown reason.
+ * <p>
+ * The caller should retry a message that failed with this response.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0;
+
+ /**
+ * The remote service associated with this connection has died and the message was not
+ * properly sent/received.
+ * <p>
+ * This is considered a permanent error and the system will automatically begin the teardown and
+ * destruction of the SipDelegate. No further messages should be sent on this transport.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1;
+
+ /**
+ * The message has not been sent/received because the delegate is in the process of closing and
+ * has become unavailable. No further messages should be sent/received on this delegate.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
+
+ /**
+ * The SIP message has an invalid start line and the message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
+
+ /**
+ * One or more of the header fields in the header section of the outgoing SIP message is invalid
+ * and the SIP message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
+
+ /**
+ * The body content of the SIP message is invalid and the message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5;
+
+ /**
+ * The feature tag associated with the outgoing message does not match any known feature tags
+ * and this message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6;
+
+ /**
+ * The feature tag associated with the outgoing message is not enabled for the associated
+ * SipDelegateConnection and can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7;
+
+ /**
+ * The link to the network has been lost and the outgoing message has failed to send.
+ * <p>
+ * This message should be retried when connectivity to the network is re-established. See
+ * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8;
+
+ /**
+ * The outgoing SIP message has not been sent due to the SipDelegate not being registered for
+ * IMS at this time.
+ * <p>
+ * This is considered a temporary failure, the message should not be retried until an IMS
+ * registration change callback is received via
+ * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged}
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
+
+ /**
+ * The outgoing SIP message has not been sent because the {@link SipDelegateImsConfiguration}
+ * version associated with the outgoing {@link SipMessage} is now stale and has failed
+ * validation checks.
+ * <p>
+ * The @link SipMessage} should be recreated using the newest
+ * {@link SipDelegateImsConfiguration} and sent again.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
+
+ /**
+ * The outgoing SIP message has not been sent because the internal state of the associated
+ * {@link SipDelegate} is changing and has temporarily brought the transport down.
+ * <p>
+ * This is considered a temporary error and the {@link SipDelegateConnection} should resend the
+ * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is
+ * no longer reported.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MESSAGE_FAILURE_REASON_", value = {
+ MESSAGE_FAILURE_REASON_UNKNOWN,
+ MESSAGE_FAILURE_REASON_DELEGATE_DEAD,
+ MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+ MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+ MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+ MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT,
+ MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+ MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE,
+ MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE,
+ MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+ MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+ MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION
+ })
+ public @interface MessageFailureReason {}
+
+
+ /**
+ * Access to use this feature tag has been denied for an unknown reason.
+ * @hide
+ */
+ public static final int DENIED_REASON_UNKNOWN = 0;
+
+ /**
+ * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by
+ * another SipDelegateConnection and can not be associated with this delegate. The feature tag
+ * will stay in this state until the feature tag is release by the other application.
+ * @hide
+ */
+ public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1;
+
+ /**
+ * Access to use this feature tag has been denied because this application does not have the
+ * permissions required to access this feature tag.
+ * @hide
+ */
+ public static final int DENIED_REASON_NOT_ALLOWED = 2;
+
+ /**
+ * Access to use this feature tag has been denied because single registration is not allowed by
+ * the carrier at this time. The application should fall back to dual registration if
+ * applicable.
+ * @hide
+ */
+ public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3;
+
+ /**
+ * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been
+ * denied.
+ * @hide
+ */
+ public static final int DENIED_REASON_INVALID = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DENIED_REASON_", value = {
+ DENIED_REASON_UNKNOWN,
+ DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
+ DENIED_REASON_NOT_ALLOWED,
+ DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED,
+ DENIED_REASON_INVALID
+ })
+ public @interface DeniedReason {}
+
+ /**
+ * The SipDelegate has closed due to an unknown reason.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0;
+
+ /**
+ * The SipDelegate has closed because the IMS service has died unexpectedly.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1;
+
+ /**
+ * The SipDelegate has closed because the IMS application has requested that the connection be
+ * destroyed.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
+
+ /**
+ * The SipDelegate has closed because the IMS service does not support the creation of
+ * SipDelegates.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED = 3;
+
+ /**
+ * The SipDelegate has been closed due to the user disabling RCS.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SIP_DELEGATE_DESTROY_REASON", value = {
+ SIP_DELEGATE_DESTROY_REASON_UNKNOWN,
+ SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD,
+ SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+ SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED,
+ SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS
+ })
+ public @interface SipDelegateDestroyReason {}
+
private final Context mContext;
private final int mSubId;
+ private final BinderCacheManager<IImsRcsController> mBinderCache;
/**
* Only visible for testing. To instantiate an instance of this class, please use
@@ -47,9 +269,11 @@
* @hide
*/
@VisibleForTesting
- public SipDelegateManager(Context context, int subId) {
+ public SipDelegateManager(Context context, int subId,
+ BinderCacheManager<IImsRcsController> binderCache) {
mContext = context;
mSubId = subId;
+ mBinderCache = binderCache;
}
/**
@@ -69,7 +293,7 @@
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isSupported() throws ImsException {
try {
- IImsRcsController controller = getIImsRcsController();
+ IImsRcsController controller = mBinderCache.getBinder();
if (controller == null) {
throw new ImsException("Telephony server is down",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
@@ -83,11 +307,90 @@
}
}
- private IImsRcsController getIImsRcsController() {
- IBinder binder = TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getTelephonyImsServiceRegisterer()
- .get();
- return IImsRcsController.Stub.asInterface(binder);
+ /**
+ * Request that the ImsService implementation create a SipDelegate, which will configure the
+ * ImsService to forward SIP traffic that matches the filtering criteria set in supplied
+ * {@link DelegateRequest} to the application that the supplied callbacks are registered for.
+ * <p>
+ * This API requires that the caller is running as part of a long-running process and will
+ * always be available to handle incoming messages. One mechanism that can be used for this is
+ * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps
+ * a persistent binding to when the app is the default SMS application.
+ * @param request The parameters that are associated with the SipDelegate creation request that
+ * will be used to create the SipDelegate connection.
+ * @param executor The executor that will be used to call the callbacks associated with this
+ * SipDelegate.
+ * @param dc The callback that will be used to notify the listener of the creation/destruction
+ * of the remote SipDelegate as well as changes to the state of the remote SipDelegate
+ * connection.
+ * @param mc The callback that will be used to notify the listener of new incoming SIP messages
+ * as well as the status of messages that were sent by the associated
+ * SipDelegateConnection.
+ * @throws ImsException Thrown if there was a problem communicating with the ImsService
+ * associated with this SipDelegateManager. See {@link ImsException#getCode()}.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
+ @NonNull DelegateConnectionStateCallback dc,
+ @NonNull DelegateConnectionMessageCallback mc) throws ImsException {
+ if (request == null || executor == null || dc == null || mc == null) {
+ throw new IllegalArgumentException("Invalid arguments passed into createSipDelegate");
+ }
+ try {
+ SipDelegateConnectionAidlWrapper wrapper =
+ new SipDelegateConnectionAidlWrapper(executor, dc, mc);
+ IImsRcsController controller = mBinderCache.listenOnBinder(wrapper,
+ wrapper::binderDied);
+ if (controller == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ controller.createSipDelegate(mSubId, request, wrapper.getStateCallbackBinder(),
+ wrapper.getMessageCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(),
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Destroy a previously created {@link SipDelegateConnection} that was created using
+ * {@link #createSipDelegate}.
+ * <p>
+ * This will also clean up all related callbacks in the associated ImsService.
+ * @param delegateConnection The SipDelegateConnection to destroy.
+ * @param reason The reason for why this SipDelegateConnection was destroyed.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
+ @SipDelegateDestroyReason int reason) {
+
+ if (delegateConnection == null) {
+ throw new IllegalArgumentException("invalid argument passed into destroySipDelegate");
+ }
+ if (delegateConnection instanceof SipDelegateConnectionAidlWrapper) {
+ SipDelegateConnectionAidlWrapper w =
+ (SipDelegateConnectionAidlWrapper) delegateConnection;
+ try {
+ IImsRcsController c = mBinderCache.removeRunnable(w);
+ c.destroySipDelegate(mSubId, w.getSipDelegateBinder(), reason);
+ } catch (RemoteException e) {
+ // Connection to telephony died, but this will signal destruction of SipDelegate
+ // eventually anyway, so return normally.
+ try {
+ w.getStateCallbackBinder().onDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ } catch (RemoteException ignore) {
+ // Local to process.
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed"
+ + " into this method");
+ }
}
}
diff --git a/telephony/java/android/telephony/ims/SipMessage.aidl b/telephony/java/android/telephony/ims/SipMessage.aidl
new file mode 100644
index 0000000..5140f8a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+parcelable SipMessage;
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
new file mode 100644
index 0000000..c3b1be2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP
+ * messages are structured and used.
+ * <p>
+ * The SIP message is represented in a partially encoded form in order to allow for easier
+ * verification and should not be used as a generic SIP message container.
+ * @hide
+ */
+public final class SipMessage implements Parcelable {
+ // Should not be set to true for production!
+ private static final boolean IS_DEBUGGING = Build.IS_ENG;
+
+ private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
+ "BYE", "CANCEL", "REGISTER"};
+
+ private final String mStartLine;
+ private final String mHeaderSection;
+ private final byte[] mContent;
+
+ /**
+ * Represents a partially encoded SIP message.
+ *
+ * @param startLine The start line of the message, containing either the request-line or
+ * status-line.
+ * @param headerSection A String containing the full unencoded SIP message header.
+ * @param content UTF-8 encoded SIP message body.
+ */
+ public SipMessage(@NonNull String startLine, @NonNull String headerSection,
+ @NonNull byte[] content) {
+ if (startLine == null || headerSection == null || content == null) {
+ throw new IllegalArgumentException("One or more null parameters entered");
+ }
+ mStartLine = startLine;
+ mHeaderSection = headerSection;
+ mContent = content;
+ }
+
+ /**
+ * Private constructor used only for unparcelling.
+ */
+ private SipMessage(Parcel source) {
+ mStartLine = source.readString();
+ mHeaderSection = source.readString();
+ mContent = new byte[source.readInt()];
+ source.readByteArray(mContent);
+ }
+ /**
+ * @return The start line of the SIP message, which contains either the request-line or
+ * status-line.
+ */
+ public @NonNull String getStartLine() {
+ return mStartLine;
+ }
+
+ /**
+ * @return The full, unencoded header section of the SIP message.
+ */
+ public @NonNull String getHeaderSection() {
+ return mHeaderSection;
+ }
+
+ /**
+ * @return only the UTF-8 encoded SIP message body.
+ */
+ public @NonNull byte[] getContent() {
+ return mContent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mStartLine);
+ dest.writeString(mHeaderSection);
+ dest.writeInt(mContent.length);
+ dest.writeByteArray(mContent);
+ }
+
+ public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
+ @Override
+ public SipMessage createFromParcel(Parcel source) {
+ return new SipMessage(source);
+ }
+
+ @Override
+ public SipMessage[] newArray(int size) {
+ return new SipMessage[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("StartLine: [");
+ if (IS_DEBUGGING) {
+ b.append(mStartLine);
+ } else {
+ b.append(sanitizeStartLineRequest(mStartLine));
+ }
+ b.append("], [");
+ b.append("Header: [");
+ if (IS_DEBUGGING) {
+ b.append(mHeaderSection);
+ } else {
+ // only identify transaction id/call ID when it is available.
+ b.append("***");
+ }
+ b.append("], ");
+ b.append("Content: [NOT SHOWN]");
+ return b.toString();
+ }
+
+ /**
+ * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF.
+ * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII.
+ */
+ private String sanitizeStartLineRequest(String startLine) {
+ String[] splitLine = startLine.split(" ");
+ if (splitLine == null || splitLine.length == 0) {
+ return "(INVALID STARTLINE)";
+ }
+ for (String method : SIP_REQUEST_METHODS) {
+ if (splitLine[0].contains(method)) {
+ return splitLine[0] + " <Request-URI> " + splitLine[2];
+ }
+ }
+ return startLine;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index b9a6b3c..37fec7a 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -21,6 +21,7 @@
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
@@ -29,6 +30,8 @@
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsUt;
+import java.util.List;
+
/**
* See MmTelFeature for more information.
* {@hide}
@@ -37,6 +40,7 @@
void setListener(IImsMmTelListener l);
int getFeatureState();
ImsCallProfile createCallProfile(int callSessionType, int callType);
+ void changeOfferedRtpHeaderExtensionTypes(in List<RtpHeaderExtensionType> types);
IImsCallSession createCallSession(in ImsCallProfile profile);
int shouldProcessCall(in String[] uris);
IImsUt getUtInterface();
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 8e84e93..f218e35 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -17,10 +17,15 @@
package android.telephony.ims.aidl;
import android.net.Uri;
+import android.telephony.ims.DelegateRequest;
import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
import com.android.ims.ImsFeatureContainer;
import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -58,6 +63,10 @@
// SipDelegateManager
boolean isSipDelegateSupported(int subId);
+ void createSipDelegate(int subId, in DelegateRequest request,
+ ISipDelegateConnectionStateCallback delegateState,
+ ISipDelegateMessageCallback delegateMessage);
+ void destroySipDelegate(int subId, ISipDelegate connection, int reason);
// Internal commands that should not be made public
void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
new file mode 100644
index 0000000..477ee95
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.aidl;
+
+import android.telephony.ims.SipMessage;
+
+/**
+ * See {@link SipDelegate} and {@link SipDelegateConnection} for docs regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegate {
+ void sendMessage(in SipMessage sipMessage, int configVersion);
+ void notifyMessageReceived(in String viaTransactionId);
+ void notifyMessageReceiveError(in String viaTransactionId, int reason);
+
+ // only used by SipDelegate.
+ void closeDialog(in String callId);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
new file mode 100644
index 0000000..ddfcb99
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.aidl;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.aidl.ISipDelegate;
+
+/**
+ * See {@link SipDelegateConnectionStateCallback} for docs regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegateConnectionStateCallback {
+ void onCreated(ISipDelegate c);
+ void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState,
+ in List<FeatureTagState> deniedFeatureTags);
+ void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+ void onDestroyed(int reason);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl
new file mode 100644
index 0000000..30b7d6c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.aidl;
+
+import android.telephony.ims.SipMessage;
+
+/**
+ * See {@link DelegateMessageCallback} and {@link DelegateConnectionMessageCallback} for docs
+ * regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegateMessageCallback {
+ void onMessageReceived(in SipMessage message);
+ void onMessageSent(in String viaTransactionId);
+ void onMessageSendFailure(in String viaTransactionId, int reason);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
new file mode 100644
index 0000000..609ee26
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.aidl;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.aidl.ISipDelegate;
+
+/**
+ * See {@link SipDelegateStateCallback} for docs regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegateStateCallback {
+ void onCreated(ISipDelegate c, in List<FeatureTagState> deniedFeatureTags);
+ void onFeatureTagRegistrationChanged(in DelegateRegistrationState registrationState);
+ void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+ void onDestroyed(int reason);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
index fe23343..cd88839 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
@@ -16,9 +16,17 @@
package android.telephony.ims.aidl;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
+
/**
* Interface for commands to the SIP Transport implementation.
* {@hide}
*/
-interface ISipTransport {
+oneway interface ISipTransport {
+ void createSipDelegate(in DelegateRequest request, ISipDelegateStateCallback dc,
+ ISipDelegateMessageCallback mc);
+ void destroySipDelegate(ISipDelegate delegate, int reason);
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
new file mode 100644
index 0000000..a7f62cc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements
+ * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called
+ * in order to trampoline events back to telephony.
+ * @hide
+ */
+public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback {
+
+ private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
+ @Override
+ public void sendMessage(SipMessage sipMessage, int configVersion) {
+ SipDelegate d = mDelegate;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> d.sendMessage(sipMessage, configVersion));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void notifyMessageReceived(String viaTransactionId) {
+ SipDelegate d = mDelegate;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> d.notifyMessageReceived(viaTransactionId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ }
+
+ @Override
+ public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+ SipDelegate d = mDelegate;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> d.notifyMessageReceiveError(viaTransactionId, reason));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ }
+
+ @Override
+ public void closeDialog(String callId) {
+ SipDelegate d = mDelegate;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> d.closeDialog(callId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ private final ISipDelegateMessageCallback mMessageBinder;
+ private final ISipDelegateStateCallback mStateBinder;
+ private final Executor mExecutor;
+
+ private volatile SipDelegate mDelegate;
+
+ public SipDelegateAidlWrapper(Executor executor, ISipDelegateStateCallback stateBinder,
+ ISipDelegateMessageCallback messageBinder) {
+ mExecutor = executor;
+ mStateBinder = stateBinder;
+ mMessageBinder = messageBinder;
+ }
+
+ @Override
+ public void onMessageReceived(SipMessage message) {
+ try {
+ mMessageBinder.onMessageReceived(message);
+ } catch (RemoteException e) {
+ // BinderDied will be called on SipTransport instance to trigger destruction. Notify
+ // failure message failure locally for now.
+ SipDelegate d = mDelegate;
+ if (d != null) {
+ notifyLocalMessageFailedToBeReceived(message,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+ }
+ }
+ }
+
+ @Override
+ public void onMessageSent(String viaTransactionId) {
+ try {
+ mMessageBinder.onMessageSent(viaTransactionId);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
+ public void onMessageSendFailure(String viaTransactionId, int reason) {
+ try {
+ mMessageBinder.onMessageSendFailure(viaTransactionId, reason);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
+ public void onCreated(@NonNull SipDelegate delegate,
+ @Nullable List<FeatureTagState> deniedTags) {
+ mDelegate = delegate;
+ try {
+ mStateBinder.onCreated(mDelegateBinder, deniedTags);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
+ public void onFeatureTagRegistrationChanged(DelegateRegistrationState registrationState) {
+ try {
+ mStateBinder.onFeatureTagRegistrationChanged(registrationState);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
+ public void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config) {
+ try {
+ mStateBinder.onImsConfigurationChanged(config);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
+ public void onDestroyed(int reasonCode) {
+ mDelegate = null;
+ try {
+ mStateBinder.onDestroyed(reasonCode);
+ } catch (RemoteException e) {
+ // Do not worry about this if the remote side is already dead.
+ }
+ }
+
+ public SipDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ public ISipDelegate getDelegateBinder() {
+ return mDelegateBinder;
+ }
+
+ private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
+ //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
+ // transaction ID can not be parsed.
+ SipDelegate d = mDelegate;
+ if (d != null) {
+ mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason));
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
new file mode 100644
index 0000000..3bd1a46
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.aidl;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Wrapper class implementing {@link SipDelegateConnection} using AIDL, which is returned to the
+ * local process. Also holds a reference to incoming connection message and state AIDL impl to
+ * trampoline events to callbacks as well as notify the local process in the event that the remote
+ * process becomes unavailable.
+ * <p>
+ * When the remote {@link SipDelegate} is created, this instance tracks the
+ * {@link ISipDelegate} associated with it and implements the
+ * {@link SipDelegateConnection} sent back to the local callback.
+ * @hide
+ */
+public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection,
+ IBinder.DeathRecipient {
+ private static final String LOG_TAG = "SipDelegateCAW";
+
+ private final ISipDelegateConnectionStateCallback.Stub mStateBinder =
+ new ISipDelegateConnectionStateCallback.Stub() {
+ @Override
+ public void onCreated(ISipDelegate c) {
+ associateSipDelegate(c);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState,
+ List<FeatureTagState> deniedFeatureTags) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onFeatureTagStatusChanged(registrationState,
+ new ArraySet<>(deniedFeatureTags)));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onImsConfigurationChanged(SipDelegateImsConfiguration registeredSipConfig) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onImsConfigurationChanged(registeredSipConfig));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onDestroyed(int reason) {
+ invalidateSipDelegateBinder();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onDestroyed(reason));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ private final ISipDelegateMessageCallback.Stub mMessageBinder =
+ new ISipDelegateMessageCallback.Stub() {
+ @Override
+ public void onMessageReceived(SipMessage message) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mMessageCallback.onMessageReceived(message));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onMessageSent(String viaTransactionId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mMessageCallback.onMessageSent(viaTransactionId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onMessageSendFailure(String viaTransactionId, int reason) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mMessageCallback.onMessageSendFailure(viaTransactionId, reason));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+
+ private final Executor mExecutor;
+ private final DelegateConnectionStateCallback mStateCallback;
+ private final DelegateConnectionMessageCallback mMessageCallback;
+ private final AtomicReference<ISipDelegate> mDelegateBinder =
+ new AtomicReference<>();
+
+ /**
+ * Wrap the local state and message callbacks, calling the implementation of these interfaces
+ * when the remote process calls these methods.
+ */
+ public SipDelegateConnectionAidlWrapper(Executor executor,
+ DelegateConnectionStateCallback stateCallback,
+ DelegateConnectionMessageCallback messageCallback) {
+ mExecutor = executor;
+ mStateCallback = stateCallback;
+ mMessageCallback = messageCallback;
+ }
+
+ @Override
+ public void sendMessage(SipMessage sipMessage, int configVersion) {
+ try {
+ ISipDelegate conn = getSipDelegateBinder();
+ if (conn == null) {
+ notifyLocalMessageFailedToSend(sipMessage,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+ return;
+ }
+ conn.sendMessage(sipMessage, configVersion);
+ } catch (RemoteException e) {
+ notifyLocalMessageFailedToSend(sipMessage,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+ }
+ }
+
+ @Override
+ public void notifyMessageReceived(String viaTransactionId) {
+ try {
+ ISipDelegate conn = getSipDelegateBinder();
+ if (conn == null) {
+ return;
+ }
+ conn.notifyMessageReceived(viaTransactionId);
+ } catch (RemoteException e) {
+ // Nothing to do here, app will eventually get remote death callback.
+ }
+ }
+
+ @Override
+ public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+ try {
+ ISipDelegate conn = getSipDelegateBinder();
+ if (conn == null) {
+ return;
+ }
+ conn.notifyMessageReceiveError(viaTransactionId, reason);
+ } catch (RemoteException e) {
+ // Nothing to do here, app will eventually get remote death callback.
+ }
+ }
+
+ // Also called upon IImsRcsController death (telephony process dies).
+ @Override
+ public void binderDied() {
+ invalidateSipDelegateBinder();
+ mExecutor.execute(() -> mStateCallback.onDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD));
+ }
+
+ /**
+ * @return Implementation of state binder.
+ */
+ public ISipDelegateConnectionStateCallback getStateCallbackBinder() {
+ return mStateBinder;
+ }
+
+ /**
+ * @return Implementation of message binder.
+ */
+ public ISipDelegateMessageCallback getMessageCallbackBinder() {
+ return mMessageBinder;
+ }
+
+ /**
+ * @return The ISipDelegateConnection associated with this wrapper.
+ */
+ public ISipDelegate getSipDelegateBinder() {
+ return mDelegateBinder.get();
+ }
+
+ private void associateSipDelegate(ISipDelegate c) {
+ if (c != null) {
+ try {
+ c.asBinder().linkToDeath(this, 0 /*flags*/);
+ } catch (RemoteException e) {
+ // already dead.
+ c = null;
+ }
+ }
+ mDelegateBinder.set(c);
+ }
+
+ private void invalidateSipDelegateBinder() {
+ ISipDelegate oldVal = mDelegateBinder.getAndUpdate((unused) -> null);
+ if (oldVal != null) {
+ try {
+ oldVal.asBinder().unlinkToDeath(this, 0 /*flags*/);
+ } catch (NoSuchElementException e) {
+ Log.i(LOG_TAG, "invalidateSipDelegateBinder: " + e);
+ }
+ }
+ }
+
+ private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
+ //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
+ // transaction ID can not be parsed.
+ mExecutor.execute(() ->
+ mMessageCallback.onMessageSendFailure(null, reason));
+ }
+}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b0a7b62..96ca022 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -509,6 +509,7 @@
* @return true if the capability is enabled, false otherwise.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
/**
@@ -547,5 +548,6 @@
* @return Binder instance that the framework will use to communicate with this feature.
* @hide
*/
+ @SuppressWarnings("HiddenAbstractMethod")
protected abstract IInterface getBinder();
}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 508d1a7..e823417 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -27,6 +27,8 @@
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
@@ -37,6 +39,7 @@
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.ArraySet;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
@@ -45,6 +48,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
/**
* Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
@@ -93,6 +98,18 @@
}
@Override
+ public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
+ throws RemoteException {
+ synchronized (mLock) {
+ try {
+ MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types));
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+ }
+
+ @Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
synchronized (mLock) {
return createCallSessionInterface(profile);
@@ -623,6 +640,24 @@
}
/**
+ * Called by the framework to report a change to the RTP header extension types which should be
+ * offered during SDP negotiation (see RFC8285 for more information).
+ * <p>
+ * The {@link ImsService} should report the RTP header extensions which were accepted during
+ * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}.
+ *
+ * @param extensionTypes The RTP header extensions the framework wishes to offer during
+ * outgoing and incoming call setup. An empty list indicates that there
+ * are no framework defined RTP header extension types to offer.
+ * @hide
+ */
+ @SystemApi
+ public void changeOfferedRtpHeaderExtensionTypes(
+ @NonNull Set<RtpHeaderExtensionType> extensionTypes) {
+ // Base implementation - should be overridden if RTP header extension handling is supported.
+ }
+
+ /**
* @hide
*/
public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
new file mode 100644
index 0000000..59f9601
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The callback associated with a {@link SipDelegateConnection}, which handles newly received
+ * messages as well as the result of sending a SIP message.
+ * @hide
+ */
+public interface DelegateConnectionMessageCallback {
+
+ /**
+ * A new {@link SipMessage} has been received from the delegate.
+ * @param message the {@link SipMessage} routed to this RCS application.
+ */
+ void onMessageReceived(@NonNull SipMessage message);
+
+ /**
+ * A message previously sent to the SIP delegate using
+ * {@link SipDelegateConnection#sendMessage} has been successfully sent.
+ * @param viaTransactionId The transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ */
+ void onMessageSent(@NonNull String viaTransactionId);
+
+ /**
+ * A message previously sent to the SIP delegate using
+ * {@link SipDelegateConnection#sendMessage} has failed to be sent.
+ * @param viaTransactionId The Transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ * @param reason The reason for the failure.
+ */
+ void onMessageSendFailure(String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
new file mode 100644
index 0000000..9761805
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+
+import java.util.Set;
+
+/**
+ * The callback associated with a {@link SipDelegateConnection} that manages the state of the
+ * SipDelegateConnection.
+ * <p>
+ * After {@link SipDelegateManager#createSipDelegate} is used to request a new
+ * {@link SipDelegateConnection} be created, {@link #onCreated} will be called with the
+ * {@link SipDelegateConnection} instance that must be used to communicate with the remote
+ * {@link SipDelegate}.
+ * <p>
+ * After, {@link #onFeatureTagStatusChanged} will always be called at least once with the current
+ * status of the feature tags that have been requested. The application may receive multiple
+ * {@link #onFeatureTagStatusChanged} callbacks over the lifetime of the associated
+ * {@link SipDelegateConnection}, which will signal changes to how SIP messages associated with
+ * those feature tags will be handled.
+ * <p>
+ * In order to start sending SIP messages, the SIP configuration parameters will need to be
+ * received, so the messaging application should make no assumptions about these parameters and wait
+ * until {@link #onImsConfigurationChanged(SipDelegateImsConfiguration)} has been called. This is
+ * guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one
+ * feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all
+ * feature tags were denied, no IMS configuration will be sent.
+ * <p>
+ * The {@link SipDelegateConnection} will stay associated with this RCS application until either the
+ * RCS application calls {@link SipDelegateManager#destroySipDelegate} or telephony destroys the
+ * {@link SipDelegateConnection}. In both cases, {@link #onDestroyed(int)} will be called.
+ * Telephony destroying the {@link SipDelegateConnection} instance is rare and will only happen in
+ * rare cases, such as if telephony itself or IMS service dies unexpectedly. See
+ * {@link SipDelegateManager.SipDelegateDestroyReason} reasons for more information on all of the
+ * cases that will trigger the {@link SipDelegateConnection} to be destroyed.
+ *
+ * @hide
+ */
+public interface DelegateConnectionStateCallback {
+
+ /**
+ * A {@link SipDelegateConnection} has been successfully created for the
+ * {@link DelegateRequest} used when calling {@link SipDelegateManager#createSipDelegate}.
+ */
+ void onCreated(@NonNull SipDelegateConnection c);
+
+ /**
+ * The status of the RCS feature tags that were requested as part of the initial
+ * {@link DelegateRequest}.
+ * <p>
+ * There are four states that each RCS feature tag can be in: registered, deregistering,
+ * deregistered, and denied.
+ * <p>
+ * When a feature tag is considered registered, SIP messages associated with that feature tag
+ * may be sent and received freely.
+ * <p>
+ * When a feature tag is deregistering, the network IMS registration still contains the feature
+ * tag, however the IMS service and associated {@link SipDelegate} is in the progress of
+ * modifying the IMS registration to remove this feature tag and requires the application to
+ * perform an action before the IMS registration can change. The specific action required for
+ * the SipDelegate to continue modifying the IMS registration can be found in the definition of
+ * each {@link DelegateRegistrationState.DeregisteringReason}.
+ * <p>
+ * When a feature tag is in the deregistered state, new out-of-dialog SIP messages for that
+ * feature tag will be rejected, however due to network race conditions, the RCS application
+ * should still be able to handle new out-of-dialog SIP requests from the network. This may not
+ * be possible, however, if the IMS registration itself was lost. See the
+ * {@link DelegateRegistrationState.DeregisteredReason} reasons for more information on how SIP
+ * messages are handled in each of these cases.
+ * <p>
+ * If a feature tag is denied, no incoming messages will be routed to the associated
+ * {@link DelegateConnectionMessageCallback} and all outgoing SIP messages related to this
+ * feature tag will be rejected. See {@link SipDelegateManager.DeniedReason}
+ * reasons for more information about the conditions when this will happen.
+ * <p>
+ * The set of feature tags contained in the registered, deregistering, deregistered, and denied
+ * lists will always equal the set of feature tags requested in the initial
+ * {@link DelegateRequest}.
+ * <p>
+ * Transitions of feature tags from registered, deregistering, and deregistered and vice-versa
+ * may happen quite often, however transitions to/from denied are rare and only occur if the
+ * user has changed the role of your application to add/remove support for one or more requested
+ * feature tags or carrier provisioning has enabled or disabled single registration entirely.
+ * Please see {@link SipDelegateManager.DeniedReason} reasons for an explanation of each of
+ * these cases as well as what may cause them to change.
+ *
+ * @param registrationState The new IMS registration state of each of the feature tags
+ * associated with the {@link SipDelegate}.
+ * @param deniedFeatureTags A list of {@link FeatureTagState} objects, each containing a feature
+ * tag associated with this {@link SipDelegateConnection} that has no access to
+ * send/receive SIP messages as well as a reason for why the feature tag is denied. For more
+ * information on the reason why the feature tag was denied access, see the
+ * {@link SipDelegateManager.DeniedReason} reasons.
+ */
+ void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState,
+ @NonNull Set<FeatureTagState> deniedFeatureTags);
+
+
+ /**
+ * IMS configuration of the underlying IMS stack used by this IMS application for construction
+ * of the SIP messages that will be sent over the carrier's network.
+ * <p>
+ * There should never be assumptions made about the configuration of the underling IMS stack and
+ * the IMS application should wait for this indication before sending out any outgoing SIP
+ * messages.
+ * <p>
+ * Configuration may change due to IMS registration changes as well as
+ * other optional events on the carrier network. If IMS stack is already registered at the time
+ * of callback registration, then this method shall be invoked with the current configuration.
+ * Otherwise, there may be a delay in this method being called if initial IMS registration has
+ * not compleed yet.
+ *
+ * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+ */
+ void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration registeredSipConfig);
+
+ /**
+ * The previously created {@link SipDelegateConnection} instance delivered via
+ * {@link #onCreated(SipDelegateConnection)} has been destroyed. This interface should no longer
+ * be used for any SIP message handling.
+ *
+ * @param reason The reason for the failure.
+ */
+ void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 12abdd1..a6f5c45 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -17,6 +17,8 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.Uri;
import android.os.RemoteException;
@@ -126,6 +128,57 @@
}
/**
+ * Called by the framework to request that the ImsService perform the network registration
+ * of all SIP delegates associated with this ImsService.
+ * <p>
+ * If the SIP delegate feature tag configuration has changed, then this method will be
+ * called in order to let the ImsService know that it can pick up these changes in the IMS
+ * registration.
+ * @hide
+ */
+ public void updateSipDelegateRegistration() {
+ // Stub implementation, ImsService should implement this
+ }
+
+
+ /**
+ * Called by the framework to request that the ImsService perform the network deregistration of
+ * all SIP delegates associated with this ImsService.
+ * <p>
+ * This is typically called in situations where the user has changed the configuration of the
+ * device (for example, the default messaging application) and the framework is reconfiguring
+ * the tags associated with each IMS application.
+ * <p>
+ * This should not affect the registration of features managed by the ImsService itself, such as
+ * feature tags related to MMTEL registration.
+ * @hide
+ */
+ public void triggerSipDelegateDeregistration() {
+ // Stub implementation, ImsService should implement this
+ }
+
+ /**
+ * Called by the framework to notify the ImsService that a SIP delegate connection has received
+ * a SIP message containing a permanent failure response (such as a 403) or an indication that a
+ * SIP response timer has timed out in response to an outgoing SIP message. This method will be
+ * called when this condition occurs to trigger the ImsService to tear down the full IMS
+ * registration and re-register again.
+ *
+ * @param sipCode The SIP error code that represents a permanent failure that was received in
+ * response to a request generated by the IMS application. See RFC3261 7.2 for the general
+ * classes of responses available here, however the codes that generate this condition may
+ * be carrier specific.
+ * @param sipReason The reason associated with the SIP error code. {@code null} if there was no
+ * reason associated with the error.
+ * @hide
+ */
+ public void triggerNetworkReregistration(@IntRange(from = 100, to = 699) int sipCode,
+ @Nullable String sipReason) {
+ // Stub implementation, ImsService should implement this
+ }
+
+
+ /**
* Notify the framework that the device is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined as
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
new file mode 100644
index 0000000..3ec9709
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The {@link SipDelegate} is implemented by the {@link ImsService} and allows a privileged
+ * IMS application to use this delegate to send SIP messages as well as acknowledge the receipt of
+ * incoming SIP messages delivered to the application over the existing IMS registration, allowing
+ * for a single IMS registration for multiple IMS applications.
+ * <p>
+ * Once the SIP delegate is created for that application,
+ * {@link ImsRegistrationImplBase#updateSipDelegateRegistration()} will be called, indicating that
+ * the application is finished setting up SipDelegates and the existing IMS registration may be
+ * modified to include the features managed by these SipDelegates.
+ * <p>
+ * This SipDelegate will need to notify the remote application of the registration of these features
+ * as well as the associated {@link SipDelegateImsConfiguration} before the application can start
+ * sending/receiving SIP messages via the transport. See
+ * {@link android.telephony.ims.DelegateStateCallback} for more information.
+ * @hide
+ */
+public interface SipDelegate {
+
+ /**
+ * The framework calls this method when a remote RCS application wishes to send a new outgoing
+ * SIP message.
+ * <p>
+ * Once sent, this SIP delegate should notify the remote application of the success or
+ * failure using {@link DelegateMessageCallback#onMessageSent(String)} or
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)}.
+ * @param message The SIP message to be sent over the operator’s network.
+ * @param configVersion The SipDelegateImsConfiguration version used to construct the
+ * SipMessage. See {@link SipDelegateImsConfiguration} for more information. If the
+ * version specified here does not match the most recently constructed
+ * {@link SipDelegateImsConfiguration}, this message should fail validation checks and
+ * {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
+ */
+ void sendMessage(@NonNull SipMessage message, int configVersion);
+
+ /**
+ * The framework is requesting that routing resources associated with the SIP dialog using the
+ * provided Call-ID to be cleaned up.
+ * <p>
+ * Typically a SIP Dialog close event will be signalled by that dialog receiving a BYE or 200 OK
+ * message, however, in some cases, the framework will request that the ImsService close the
+ * dialog due to the open dialog holding up an event such as applying a provisioning change or
+ * handing over to another transport type.
+ * @param callId The call-ID header value associated with the ongoing SIP Dialog that the
+ * framework is requesting be closed.
+ */
+ void closeDialog(@NonNull String callId);
+
+ /**
+ * The remote application has received the SIP message and is processing it.
+ * @param viaTransactionId The Transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ */
+ void notifyMessageReceived(@NonNull String viaTransactionId);
+
+ /**
+ * The remote application has either not received the SIP message or there was an error
+ * processing it.
+ * @param viaTransactionId The Transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ * @param reason The reason why the message was not correctly received.
+ */
+ void notifyMessageReceiveError(@NonNull String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index b2b2914..b48f631 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -18,27 +18,75 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.IBinder;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.aidl.SipDelegateAidlWrapper;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
- * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
- * IMS applications in order to support IMS single registration.
+ * The ImsService implements this class to manage the creation and destruction of
+ * {@link SipDelegate}s.
+ *
+ * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS
+ * applications as a delegate to the associated carrier's IMS Network in order to support using a
+ * single IMS registration for all MMTEL and RCS signalling traffic.
* @hide
*/
@SystemApi
public class SipTransportImplBase {
+ private static final String LOG_TAG = "SipTransportIB";
+
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mBinderExecutor.execute(() -> binderDiedInternal());
+ }
+ };
+
+ private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() {
+ @Override
+ public void createSipDelegate(DelegateRequest request, ISipDelegateStateCallback dc,
+ ISipDelegateMessageCallback mc) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mBinderExecutor.execute(() -> createSipDelegateInternal(request, dc, mc));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void destroySipDelegate(ISipDelegate delegate, int reason) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
private final Executor mBinderExecutor;
- private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() {
-
- };
+ private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
/**
* Create an implementation of SipTransportImplBase.
*
- * @param executor The executor that remote calls from the framework should be called on.
+ * @param executor The executor that remote calls from the framework will be called on. This
+ * includes the methods here as well as the methods in {@link SipDelegate}.
*/
public SipTransportImplBase(@NonNull Executor executor) {
if (executor == null) {
@@ -49,6 +97,79 @@
}
/**
+ * Called by the Telephony framework to request the creation of a new {@link SipDelegate}.
+ * <p>
+ * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with
+ * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
+ * <p>
+ * This method will be called on the Executor specified in
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+ *
+ * @param request A SIP delegate request containing the parameters that the remote RCS
+ * application wishes to use.
+ * @param dc A callback back to the remote application to be used to communicate state callbacks
+ * for the SipDelegate.
+ * @param mc A callback back to the remote application to be used to send SIP messages to the
+ * remote application and acknowledge the sending of outgoing SIP messages.
+ * @hide
+ */
+ public void createSipDelegate(@NonNull DelegateRequest request,
+ @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
+ throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+ }
+
+ /**
+ * Destroys the SipDelegate associated with a remote IMS application.
+ * <p>
+ * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be
+ * called to notify listeners of its destruction to release associated resources.
+ * <p>
+ * This method will be called on the Executor specified in
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+ * @param delegate The delegate to be destroyed.
+ * @param reason The reason the remote connection to this {@link SipDelegate} is being
+ * destroyed.
+ * @hide
+ */
+ public void destroySipDelegate(@NonNull SipDelegate delegate,
+ @SipDelegateManager.SipDelegateDestroyReason int reason) {
+ throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+ }
+
+ private void createSipDelegateInternal(DelegateRequest r, ISipDelegateStateCallback cb,
+ ISipDelegateMessageCallback mc) {
+ SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
+ mDelegates.add(wrapper);
+ createSipDelegate(r, wrapper, wrapper);
+ }
+
+ private void destroySipDelegateInternal(ISipDelegate d, int reason) {
+ SipDelegateAidlWrapper result = null;
+ for (SipDelegateAidlWrapper w : mDelegates) {
+ if (Objects.equals(d, w.getDelegateBinder())) {
+ result = w;
+ break;
+ }
+ }
+
+ if (result != null) {
+ mDelegates.remove(result);
+ destroySipDelegate(result.getDelegate(), reason);
+ } else {
+ Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to "
+ + d);
+ }
+ }
+
+ private void binderDiedInternal() {
+ for (SipDelegateAidlWrapper w : mDelegates) {
+ destroySipDelegate(w.getDelegate(),
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ }
+ mDelegates.clear();
+ }
+
+ /**
* @return The IInterface used by the framework.
* @hide
*/
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 12cec8d..a85e92c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -79,6 +79,16 @@
}
}
+fun WmAssertion.windowAlwaysVisible(
+ packageName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("windowAlwaysVisible", bugId, enabled) {
+ this.showsAppWindowOnTop(packageName)
+ }
+}
+
@JvmOverloads
fun LayersAssertion.noUncoveredRegions(
beginRotation: Int,
@@ -214,6 +224,16 @@
}
}
+fun LayersAssertion.layerAlwaysVisible(
+ packageName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("layerAlwaysVisible", bugId, enabled) {
+ this.showsLayer(packageName)
+ }
+}
+
fun EventLogAssertion.focusChanges(
vararg windows: String,
bugId: Int = 0,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index d100383..a1a7102 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -30,6 +30,8 @@
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.windowAlwaysVisible
+import com.android.server.wm.flicker.layerAlwaysVisible
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
@@ -39,6 +41,8 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
@@ -141,6 +145,8 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible(bugId = 140855415)
statusBarWindowIsAlwaysVisible(bugId = 140855415)
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ windowAlwaysVisible(configuration.intentPackageName)
}
layersTrace {
@@ -152,6 +158,9 @@
configuration.endRotation)
statusBarLayerRotatesScales(configuration.startRotation,
configuration.endRotation, enabled = false)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ enabled = configuration.startRotation == configuration.endRotation)
+ layerAlwaysVisible(configuration.intentPackageName)
}
layersTrace {
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index b8122ed..911f2fb 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -323,6 +323,7 @@
method public android.net.DhcpInfo getDhcpInfo();
method public int getMaxNumberOfNetworkSuggestionsPerApp();
method @IntRange(from=0) public int getMaxSignalLevel();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getNetworkSuggestionUserApprovalStatus();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public java.util.List<android.net.wifi.WifiNetworkSuggestion> getNetworkSuggestions();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
@@ -393,6 +394,11 @@
field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5
field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
+ field public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE = 4; // 0x4
+ field public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER = 2; // 0x2
+ field public static final int STATUS_SUGGESTION_APPROVAL_PENDING = 1; // 0x1
+ field public static final int STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER = 3; // 0x3
+ field public static final int STATUS_SUGGESTION_APPROVAL_UNKNOWN = 0; // 0x0
field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION = 1; // 0x1
field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION = 2; // 0x2
field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING = 3; // 0x3
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index dc96df6..d235c80 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -89,6 +89,8 @@
rule android.util.LocalLog* com.android.wifi.x.@0
rule android.util.Rational* com.android.wifi.x.@0
+rule android.os.BasicShellCommandHandler* com.android.wifi.x.@0
+
# Use our statically linked bouncy castle library
rule org.bouncycastle.** com.android.wifi.x.@0
# Use our statically linked protobuf library
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 7329c16..48e2312 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -275,4 +275,6 @@
void setAutoWakeupEnabled(boolean enable);
boolean isAutoWakeupEnabled();
+
+ int getNetworkSuggestionUserApprovalStatus(String packageName);
}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 73c52ab..034defb 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -173,7 +173,7 @@
/** @deprecated Use the new {@link android.net.wifi.RttManager#getRttCapabilities()} API.*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public Capabilities getCapabilities() {
throw new UnsupportedOperationException(
"getCapabilities is not supported in the adaptation layer");
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ef55a56..7e04d8f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -53,6 +53,7 @@
import android.os.RemoteException;
import android.os.WorkSource;
import android.os.connectivity.WifiActivityEnergyInfo;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.CloseGuard;
import android.util.Log;
@@ -264,6 +265,44 @@
public @interface SuggestionConnectionStatusCode {}
/**
+ * Status code if suggestion approval status is unknown, an App which hasn't made any
+ * suggestions will get this code.
+ */
+ public static final int STATUS_SUGGESTION_APPROVAL_UNKNOWN = 0;
+
+ /**
+ * Status code if the calling app is still pending user approval for suggestions.
+ */
+ public static final int STATUS_SUGGESTION_APPROVAL_PENDING = 1;
+
+ /**
+ * Status code if the calling app got the user approval for suggestions.
+ */
+ public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER = 2;
+
+ /**
+ * Status code if the calling app suggestions were rejected by the user.
+ */
+ public static final int STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER = 3;
+
+ /**
+ * Status code if the calling app was approved by virtue of being a carrier privileged app.
+ * @see TelephonyManager#hasCarrierPrivileges().
+ */
+ public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE = 4;
+
+ /** @hide */
+ @IntDef(prefix = {"STATUS_SUGGESTION_APPROVAL_"},
+ value = {STATUS_SUGGESTION_APPROVAL_UNKNOWN,
+ STATUS_SUGGESTION_APPROVAL_PENDING,
+ STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER,
+ STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER,
+ STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SuggestionUserApprovalStatus {}
+
+ /**
* Broadcast intent action indicating whether Wi-Fi scanning is currently available.
* Available extras:
* - {@link #EXTRA_SCAN_AVAILABLE}
@@ -2010,6 +2049,26 @@
}
/**
+ * Get the Suggestion approval status of the calling app. When an app makes suggestions using
+ * the {@link #addNetworkSuggestions(List)} API they may trigger a user approval flow. This API
+ * provides the current approval status.
+ *
+ * @return Status code for the user approval. One of the STATUS_SUGGESTION_APPROVAL_ values.
+ * @throws {@link SecurityException} if the caller is missing required permissions.
+ */
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public @SuggestionUserApprovalStatus int getNetworkSuggestionUserApprovalStatus() {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mService.getNetworkSuggestionUserApprovalStatus(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Add or update a Passpoint configuration. The configuration provides a credential
* for connecting to Passpoint networks that are operated by the Passpoint
* service provider specified in the configuration.
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 41d0857..7c051f0 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1267,7 +1267,7 @@
* @param bssidInfos access points to watch
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void configureWifiChange(
int rssiSampleSize, /* sample size for RSSI averaging */
int lostApSampleSize, /* samples to confirm AP's loss */
@@ -1301,7 +1301,7 @@
* provided on {@link #stopTrackingWifiChange}
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void startTrackingWifiChange(WifiChangeListener listener) {
throw new UnsupportedOperationException();
}
@@ -1312,7 +1312,7 @@
* #stopTrackingWifiChange}
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void stopTrackingWifiChange(WifiChangeListener listener) {
throw new UnsupportedOperationException();
}
@@ -1320,7 +1320,7 @@
/** @hide */
@SystemApi
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void configureWifiChange(WifiChangeSettings settings) {
throw new UnsupportedOperationException();
}
@@ -1376,7 +1376,7 @@
* also be provided on {@link #stopTrackingBssids}
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void startTrackingBssids(BssidInfo[] bssidInfos,
int apLostThreshold, BssidListener listener) {
throw new UnsupportedOperationException();
@@ -1387,7 +1387,7 @@
* @param listener same object provided in {@link #startTrackingBssids}
*/
@Deprecated
- @SuppressLint("Doclava125")
+ @SuppressLint("RequiresPermission")
public void stopTrackingBssids(BssidListener listener) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 7340b14..5729247 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2415,4 +2415,13 @@
assertFalse(mWifiManager.isScanAlwaysAvailable());
verify(mWifiService).isScanAlwaysAvailable();
}
+
+ @Test
+ public void testGetNetworkSuggestionUserApprovalStatus() throws Exception {
+ when(mWifiService.getNetworkSuggestionUserApprovalStatus(TEST_PACKAGE_NAME))
+ .thenReturn(WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER);
+ assertEquals(WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER,
+ mWifiManager.getNetworkSuggestionUserApprovalStatus());
+ verify(mWifiService).getNetworkSuggestionUserApprovalStatus(TEST_PACKAGE_NAME);
+ }
}